hdu6171 Admiral
题意:给你一个如图形状21个元素的排列,只能够通过0元素跟其上下相邻的元素进行交换,问是否可以在20步内转换成目标排列,如果可以输出最小步数,否则输出too difficult
这是目标排列,给定的初始排列可能不同
分析
首先可以看出这是一个求最短路的题,我们可以把元素的排列看成是一种状态,他可以经过若干次状态转移到达目标状态,这就是一个最短路问题了。
尝试使用普通BFS
0元素可以向四个方向移动,上左,上,下,下右,极端情况走到21步(也就是too difficult),一共有4^21的复杂度,1e12的数量级了,普通bfs必定会超时。
尝试使用双向BFS
让初始排列和目标排列一起扩展,极端情况,双方各扩展10步,也就是两个4^10,2e6的数量级,然后在双方扩展的过程中,看有没有交集。如果有交集就判断是否是20步以内,如果两个队头状态的步数相加已经超过20步了,也要结束循环。
状态怎么存储
如果我们把这个排列数组存下来,可见非常消耗内存,可以通过哈希的方式存储,可以将排列转换成字符串,也可以将排列用自建哈希函数,因为一个只有21个数,每个数是0~5,只占用3个二进制位,一共需要占用63个二进制位,而longlong有64个二进制位,所以其状态可以用一个longlong来存储。
双向BFS+string存储
#include <iostream> #include <algorithm> #include <string> #include <queue> #include <unordered_map> using namespace std; const int maxn = 1e6+10; typedef pair<int,int> pii; int T,sx,sy; string a,b; unordered_map<string,int> disa,disb; unordered_map<string,pii> posa,posb; queue<string> qa,qb; int extentA(){ string cur = qa.front();qa.pop(); int d = disa[cur]; pii p = posa[cur]; int x = p.first,y = p.second; int nx = -1,ny = -1; for(int i = 0;i<4;i++){ if(i == 0) nx = x-1,ny = y; if(i == 1) nx = x-1,ny = y-1; if(i == 2) nx = x+1,ny = y; if(i == 3) nx = x+1,ny = y+1; if(nx>=1 && nx<=6 && ny>=1 && ny<=nx){ swap(cur[x*(x-1)/2+y],cur[nx*(nx-1)/2+ny]); if(!disa[cur]){ qa.push(cur); disa[cur] = d+1; posa[cur] = {nx,ny}; if(disb.count(cur)) return disa[cur] + disb[cur]; } swap(cur[x*(x-1)/2+y],cur[nx*(nx-1)/2+ny]); } } return -1; } int bfs(){ disa.clear();disb.clear(); posa.clear();posb.clear(); while(qa.size()) qa.pop(); while(qb.size()) qb.pop(); qa.push(a);qb.push(b); disa[a] = 0,disb[b] = 0; posa[a] = {sx,sy};posb[b] = {1,1}; while(qa.size() && qb.size()){ if(disa[qa.front()] + disb[qb.front()] > 20) return -1; if(qa.size() <= qb.size()){ //谁目前扩展的少就去扩展谁 int res = extentA(); if(res!=-1 && res<=20) return res; if(res>20) return -1; }else{ //扩展B时,交换一下指针就好 swap(qa,qb); swap(disa,disb); swap(posa,posb); int res = extentA(); if(res!=-1 && res<=20) return res; if(res>20) return -1; swap(qa,qb); swap(disa,disb); swap(posa,posb); } } return -1; } int main(){ cin>>T; while(T--){ a = '-';b = "-011222333344444555555";//以字符串存储状态,下标从1开始 char c; for(int i = 1;i<=6;i++){ for(int j = 1;j<=i;j++){ scanf(" %c",&c); a+=c; if(c == '0') sx = i,sy = j; } } if(a == b) puts("0"); else{ int res = bfs(); if(res == -1) puts("too difficult"); else cout<<res<<endl; } } return 0; }
双向BFS+Hash
#include <iostream> #include <algorithm> #include <queue> #include <unordered_map> using namespace std; typedef unsigned long long ull; int dir[4][2] = {-1,0,-1,-1,1,0,1,1}; int T; struct node{ ull x,y; ull hs; int st[7][7]; }sta,stb; ull Hash(node &no){ ull hs = 0; for(int i =0;i<6;i++){ for(int j = 0;j<=i;j++){ hs = hs<<3|no.st[i][j]; } } return hs; } int ex(queue<node> &q,unordered_map<ull,int> &mp1,unordered_map<ull,int> &mp2){ node cur = q.front();q.pop(); for(int i = 0;i<4;i++){ ull dx = cur.x+dir[i][0]; ull dy = cur.y+dir[i][1]; if(dx<0 || dx>5 || dy<0 || dy> dx) continue; node ne = cur; swap(ne.st[cur.x][cur.y],ne.st[dx][dy]); ne.x = dx,ne.y = dy,ne.hs = Hash(ne); if(!mp1.count(ne.hs)) { mp1[ne.hs] = mp1[cur.hs]+1; q.push(ne); if(mp2.count(ne.hs)) return mp1[ne.hs] + mp2[ne.hs]; } } return -1; } int bfs(){ queue<node> qa,qb; unordered_map<ull,int> disa,disb; qa.push(sta);qb.push(stb); disa[sta.hs] = 0;disb[stb.hs] = 0; while(qa.size() && qb.size()){ if(disa[qa.front().hs] + disb[qb.front().hs]>20) return -1; int res; if(qa.size()<= qb.size()){ res = ex(qa,disa,disb); }else{ res = ex(qb,disb,disa); } if(res!=-1 && res<=20) return res; if(res>20) return -1;//需注意 } return -1; } int main(){ cin>>T; while(T--){ for(int i = 0;i<6;i++){ for(int j = 0;j<=i;j++){ scanf("%d",&sta.st[i][j]); stb.st[i][j] = i; if(sta.st[i][j] == 0){ sta.x = i,sta.y = j; } } } stb.x = 0,stb.y = 0; stb.hs = Hash(stb); sta.hs = Hash(sta); if(sta.hs == stb.hs) puts("0"); //初始状态也需要判断 else{ int res = bfs(); if(res == -1) puts("too difficult"); else cout<<res<<endl; } } return 0; }