小进阶 关于memset 它是按照字节对内存块初始化 对于int数组 只能初始化为0或-1 否则不稳定 其它情况还是for来初始化比较妥当 初始化一个0x3f 可以当无穷大使用
因为调用库会有点慢 所以应学会数组模拟队列 快速复习一下 int q[N],hh=0,tt=-1; //hh队头 tt队尾 头在左边便于数组操作 //插入 q[++tt]=x; //弹出 hh++; 队头指针直接右移一个位置 //判断是否为空 if(hh<=tt) not empty else empty
bfs中队列的两个性质 1.队列里最多存在两种状态 当取出队头 队尾就会新增x+1 2.单调性 前一段状态为x 后一段状态为x+1
双端队列 由bfs队列中性质 若是走白给的点 那么就把这个点插到队头继续搜 如果是要费用的点 插到队尾 这样就保证了单调性
[洛谷4554] [c++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 int n,m,a,b,x1,y3,x2,y2; char g[510][510]; int dist[510][510]; int dx[]={1,-1,0,0}; int dy[]={0,0,1,-1}; typedef pair <int,int> PII; deque <PII> q; //双端队列 int bfs(int x,int y) { q.push_back({x,y}); dist[x][y]=0; while(q.size()) { auto t=q.front(); q.pop_front(); char ch=g[t.first][t.second]; for(int i=0;i<4;i++) { int a=t.first+dx[i]; int b=t.second+dy[i]; if(a<0 || a>=n || b<0 || b>=m) continue; if(dist[a][b]>=0 ) continue; if(g[a][b]==ch) { dist[a][b]=dist[t.first][t.second]; //如果有点可以走且符号一样 费用为0 q.push_front({a,b}); //这个点插到最前面优先搜 } if(g[a][b]!=ch) { dist[a][b]=dist[t.first][t.second]+1; q.push_back({a,b}); //如果这点要花费 那么就从 } if(a==x2 && b==y2) return dist[x2][y2]; } } return -1; } int main() { while(cin>>n>>m, n||m ) { //cout<<n<<" "<<m<<endl; for(int i=0;i<n;i++) { scanf("%s",g[i]); } memset(dist,-1,sizeof dist); q.clear(); cin>>x1>>y3>>x2>>y2; int res=bfs(x1,y3); cout<<res<<endl; } return 0; }
1.首先 棋盘那么多变化 肯定是存不了的 可以用字符串来表示这个棋盘 这便是一个状态压缩 然后需要一维数组到二维数组 二维数组到一维数组的变化 这是个小技巧 2. map 把一个string 对应一个int 存步骤数 3. count看map里有没有已经出现过下一个要走的点 如果已经出现过就没必要走了 4. 需要回溯 因为是对于一个点四个方向去看
[洛谷1379八数码难题] [c++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include<bits/stdc++.h> using namespace std; string end1="123804765"; int dx[]={0,0,1,-1}; int dy[]={1,-1,0,0}; unordered_map<string,int> dist; queue<string> q; int bfs(string start) { q.push(start); dist[start]=0; while(q.size()) { auto t=q.front(); q.pop(); if(t==end1) return dist[t]; int distance=dist[t]; int a=t.find('0'); //找0的下标 int x1=a/3,y1=a%3;//一维下标转二维 for(int i=0;i<4;i++) { int x2=x1+dx[i]; int y2=y1+dy[i]; if(x2<0 || x2>=3 || y2<0 || y2>=3) continue; int tmp=x2*3 +y2; //二维下标转一维交换位置 swap(t[a],t[tmp]); if(!dist.count(t)) //看下一个位置有没有被访问过 { dist[t]=distance+1; q.push(t); } swap(t[a],t[tmp]); //必须再换回去 } } return -1; } int main() { string start; cin>>start; int res=bfs(start); cout<<res<<endl; return 0; }
如果起点和终点的状态已知 那么可以双向bfs 让终点和起点一起入队 同时开始搜 总有一个时刻 起点终点都可以拓展到 此时结束 这样空间占用更少 时间占用也更少
双向bfs维护的是两个队列而不是一个 轮流拓展两个队列 同时用数组或者哈希表记录搜索情况 给从两个方向拓展的节点以不同的标记 当某点被两种标记同时标记时 搜索结束
使用的条件 必须知道初始状态与结束状态 往往涉及如上题的状态压缩
这道之前有 用双向试一试 就是起点初始vs为1 终点为2 如果有个点vs为3则相遇了 return return 的值需要加1 因为终点初始是0 这一格没有算进去 再次注意输入的是字符
[洛谷] [c++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include<bits/stdc++.h> using namespace std; const int N=1010; int g[N][N]; //存地图 int dist[N][N]; int vs[N][N]; typedef pair<int,int> PII; queue<PII> s; int n,x1,y11,x2,y2=0; PII q[N*N]; int dx[]={1,0,-1,0}; int dy[]={0,1,0,-1}; int bfs() { memset(dist,-1,sizeof dist); memset(vs,-1,sizeof vs); dist[x1][y11]=0,dist[x2][y2]=0; vs[x1][y11]=1,vs[x2][y2]=2; q[0]={x1,y11},q[1]={x2,y2}; int hh=0,tt=1; while(hh<=tt) { auto t=q[hh++]; for(int i=0;i<4;i++) { int a=t.first+dx[i]; int b=t.second+dy[i]; if(a<1 || a>n || b<1 || b>n) continue; if(g[a][b]!=0) continue; //越界 不是马路 if(vs[a][b]+vs[t.first][t.second]==3) //说明拓展到了一个点 { return dist[t.first][t.second]+dist[a][b]+1; //记得加1 因为还有一格 } if(dist[a][b]>=0) continue; //说明走过了 dist[a][b]=dist[t.first][t.second]+1; if(vs[a][b]==-1) { vs[a][b]=vs[t.first][t.second]; } q[++tt]={a,b}; } } return -1; } int main() { cin>>n; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { char c; cin>>c; g[i][j]=c-'0'; } } //if(g[1][1]==0) cout<<2<<endl; cin>>x1>>y11>>x2>>y2; int res=bfs(); cout<<res; return 0; }
八数码也可以用双向bfs 但是就显得有点复杂了 略