ida* 的一些题 ida*小结
这些题都是http://www.cnblogs.com/ambition/archive/2011/07/25/search_plus.html 里的。。我的一些代码也参考了里边的代码
HDU 1043 Eight 话说是一道涉及到人生完整不完整的题 八数码
判断有没有解:求出所给状态的逆序数m,如果同是奇数则无解,偶数则有解。 why? 因为移动一次逆序数 加2 或 减2 (我也不会证,就是自己试了下,发现是)
在我的程序中空位用0表示
方法一: A*
估价函数: 计算当前状态到目标状态的 曼哈顿距离。 因为移动一次,曼哈顿距离 加1或减1,所以直接返回曼哈顿距离
状态存储: 采用 康托 法。 把一个图转化成一个数。
判重: 把图转化为 康托数后,用个 hash 9!=362880
然后就是bfs了。
用一个pre 数组记录路径
A* 是要用优先队列了,无奈于对堆不太熟练,于是用了STL
下面对程序的执行过程简略介绍:
对于每个结点存 了当前的图G(用了康托后就是一个数, (可以不用康托数,直接开个数组 记录整个图)我的第二个程序就是这样的。
当前已走多少步step,
当前状态到目标状态的曼哈顿距离dis,
则预计至少h步到目标状态(h=step+dis)
优先队列就是根据 按上从小到大排序的 (STL 中默认是从大到小,所以要自己写个比较函数,或者重载运算符(我是重载运算符的)
1.读入数据:
如果是x换成0
2.判断逆序数是否为奇数
奇数:无解
偶数:继续
3.bfs
1.起点入队
2.向四个方向 搜索,计算 step,dis,h
3.判重
不重,入队,设置其前趋
4.遇到终点 则结束。
输出结果,是倒着的,放在数组里。
A*代码:
1 #include<iostream> 2 #include<queue> 3 using namespace std; 4 int vis[362885]; 5 int factor[]={1,1,2,6,24,120,720,5040,40320,362880,3628800}; //用于康托 6 int mx[]={1,0,0,-1}; //四个移动方向的控制 7 int my[]={0,-1,1,0}; 8 char x[11],ans[362885];//由于BFS搜得的结果在是倒着的。所以先把移动序列正着存放在ans中 9 char cov[]={'d','l','r','u'}; 10 int na; 11 const int N=9; 12 struct node 13 { 14 int step;//到G这个状态已经走了step步 15 int dis;//用估价函数所得 16 int h; //h=step+dis 用于优先队列的优先级 17 int G; //当前状态,是一个数,用反康托算法展开就可以得到这个状态 18 }; 19 struct s 20 { 21 int sum; 22 int d; //移动方向 23 }pre[362885];//用于记录当前状态的的前驱 24 priority_queue<node> Q; 25 int Start,End; 26 bool operator<(const node &t1,const node &t2) 27 { 28 if(t1.h==t2.h) 29 return t1.step<t2.step; 30 else 31 return t1.h>t2.h; 32 } 33 int cantor(char *y,int n) //康托 把这个序列变成一个数 34 { 35 int sum=0,i,j,cnt; 36 for(i=0;i<n;i++) 37 { 38 cnt=0; 39 for(j=i+1;j<n;j++) 40 if(y[j]<y[i]) 41 cnt++; 42 sum+=factor[n-i-1]*cnt; 43 } 44 return sum; 45 } 46 void fcantor(char *y,int n,int m) //反康托,把一个数n变成一个排列 47 { 48 int i,j,cnt,k; 49 bool vis[15]={0}; 50 for(i=0;i<m;i++) 51 { 52 k=n/factor[m-i-1]; 53 n%=factor[m-i-1]; 54 cnt=0; 55 for(j=1;j<=m;j++) 56 { 57 if(!vis[j]) 58 cnt++; 59 if(cnt==k+1) 60 break; 61 } 62 y[i]=j; 63 vis[j]=true; 64 } 65 } 66 int inverse_number(char *y,int m) //求逆序数 67 { 68 int i,j,cnt=0; 69 for(i=1;i<m;i++) 70 { 71 if(y[i]==N)continue; 72 for(j=i-1;j>=0;j--) 73 if(y[j]>y[i]&&y[j]!=N) 74 cnt++; 75 } 76 return !(cnt&1); 77 } 78 int man_dis(char *y,int n) // 曼哈顿距离 79 { 80 int i,j,tx,ty,dis=0; 81 for(i=0;i<3;i++) 82 for(j=0;j<3;j++) 83 { 84 if(y[i*3+j]==9)continue; 85 tx=(y[i*3+j]-1)/3; 86 ty=(y[i*3+j]-1)%3; 87 dis+=abs(tx-i)+abs(ty-j); 88 } 89 return dis; 90 } 91 void bfs() 92 { 93 while(!Q.empty())Q.pop(); 94 memset(vis,0,sizeof(vis)); 95 node t,p; 96 t.step=0; t.G=Start; t.dis=0; 97 Q.push(t); 98 vis[Start]=true; 99 pre[Start].d=-1;pre[Start].sum=-1; 100 char y[11]; 101 int fx,fy,tx,ty,i; 102 while(!Q.empty()) 103 { 104 t=Q.top(); 105 Q.pop(); 106 fcantor(y,t.G,N); 107 for(i=0;i<N;i++) 108 if(y[i]==9) 109 break; 110 fx=i/3; fy=i%3; 111 for(i=0;i<4;i++) //四个方向 广搜 112 { 113 p=t; 114 tx=fx+mx[i]; 115 ty=fy+my[i]; 116 if(tx<0||ty<0||tx>=3||ty>=3) 117 continue; 118 swap(y[fx*3+fy],y[tx*3+ty]); //移动 119 p.G=cantor(y,N); //转化为一个数 120 p.dis=man_dis(y,N); //估价函数 121 p.step++; 122 p.h=p.step+p.dis; 123 if(!vis[p.G]) //判重 124 { 125 vis[p.G]=true; 126 Q.push(p); 127 pre[p.G].sum=t.G; 128 pre[p.G].d=i; 129 } 130 if(p.G==0) 131 return; 132 swap(y[fx*3+fy],y[tx*3+ty]); //不要忘了换回来 133 } 134 } 135 } 136 int main() 137 { 138 //freopen("input.txt","r",stdin); 139 int i; 140 while(true) 141 { 142 for(i=0;i<N;i++) 143 { 144 if(!(cin>>x[i])) 145 return 0; 146 if(x[i]=='x') 147 x[i]=N; 148 else 149 x[i]-='0'; 150 151 } 152 Start=cantor(x,N); 153 End=0; 154 if(inverse_number(x,N)) 155 { 156 bfs(); 157 na=0; 158 while(pre[End].sum!=-1) 159 { 160 ans[na++]=cov[pre[End].d]; 161 End=pre[End].sum; 162 } 163 for(i=na-1;i>=0;i--) 164 cout<<ans[i]; 165 } 166 else 167 cout<<"unsolvable"; 168 cout<<endl; 169 } 170 return 0; 171 }
存图时不用康托数,直接保存整个图的代码。这样的发处是,每次出队后,不用把数把解为图了
1 #include<iostream> 2 #include<queue> 3 using namespace std; 4 int vis[362885]; 5 int factor[]={1,1,2,6,24,120,720,5040,40320,362880,3628800}; 6 int mx[]={1,0,0,-1}; 7 int my[]={0,-1,1,0}; 8 char x[11],ans[362885]; 9 char cov[]={'d','l','r','u'}; 10 int na; 11 const int N=9; 12 struct node 13 { 14 int step; 15 int dis; 16 int h; 17 char G[11]; 18 }; 19 struct s 20 { 21 int sum; 22 int d; 23 }pre[362885]; 24 priority_queue<node> Q; 25 int Start,End; 26 bool operator<(const node &t1,const node &t2) 27 { 28 if(t1.h==t2.h) 29 return t1.step<t2.step; 30 else 31 return t1.h>t2.h; 32 } 33 int cantor(char *y,int n) 34 { 35 int sum=0,i,j,cnt; 36 for(i=0;i<n;i++) 37 { 38 cnt=0; 39 for(j=i+1;j<n;j++) 40 if(y[j]<y[i]) 41 cnt++; 42 sum+=factor[n-i-1]*cnt; 43 } 44 return sum; 45 } 46 void fcantor(char *y,int n,int m) 47 { 48 int i,j,cnt,k; 49 bool vis[15]={0}; 50 for(i=0;i<m;i++) 51 { 52 k=n/factor[m-i-1]; 53 n%=factor[m-i-1]; 54 cnt=0; 55 for(j=1;j<=m;j++) 56 { 57 if(!vis[j]) 58 cnt++; 59 if(cnt==k+1) 60 break; 61 } 62 y[i]=j; 63 vis[j]=true; 64 } 65 } 66 int inverse_number(char *y,int m) 67 { 68 int i,j,cnt=0; 69 for(i=1;i<m;i++) 70 { 71 if(y[i]==N)continue; 72 for(j=i-1;j>=0;j--) 73 if(y[j]>y[i]&&y[j]!=N) 74 cnt++; 75 } 76 return !(cnt&1); 77 } 78 int man_dis(char *y,int n) 79 { 80 int i,j,tx,ty,dis=0; 81 for(i=0;i<3;i++) 82 for(j=0;j<3;j++) 83 { 84 if(y[i*3+j]==9)continue; 85 tx=(y[i*3+j]-1)/3; 86 ty=(y[i*3+j]-1)%3; 87 dis+=abs(tx-i)+abs(ty-j); 88 } 89 return dis; 90 } 91 void bfs() 92 { 93 while(!Q.empty())Q.pop(); 94 memset(vis,0,sizeof(vis)); 95 node t,p; 96 t.step=0; t.dis=0; 97 memcpy(t.G,x,sizeof(x)); 98 Q.push(t); 99 vis[Start]=true; 100 pre[Start].d=-1;pre[Start].sum=-1; 101 char y[11]; 102 int fx,fy,tx,ty,i,fn,tn; 103 while(!Q.empty()) 104 { 105 t=Q.top(); 106 Q.pop(); 107 fn=cantor(t.G,N); 108 //cout<<t.dis<<" "<<t.step<<" "<<t.h<<endl; 109 for(i=0;i<N;i++) 110 if(t.G[i]==9) 111 break; 112 fx=i/3; fy=i%3; 113 for(i=0;i<4;i++) 114 { 115 p=t; 116 tx=fx+mx[i]; 117 ty=fy+my[i]; 118 if(tx<0||ty<0||tx>=3||ty>=3) 119 continue; 120 p.G[fx*3+fy]=p.G[tx*3+ty]; 121 p.G[tx*3+ty]=9; 122 tn=cantor(p.G,N); 123 p.dis=man_dis(p.G,N); 124 p.step++; 125 p.h=p.step+p.dis; 126 if(!vis[tn]) 127 { 128 vis[tn]=true; 129 Q.push(p); 130 pre[tn].sum=fn; 131 pre[tn].d=i; 132 } 133 if(tn==0) 134 return; 135 } 136 } 137 } 138 int main() 139 { 140 //freopen("input.txt","r",stdin); 141 int i; 142 while(true) 143 { 144 for(i=0;i<N;i++) 145 { 146 if(!(cin>>x[i])) 147 return 0; 148 if(x[i]=='x') 149 x[i]=N; 150 else 151 x[i]-='0'; 152 153 } 154 /*cout<<cantor(x,N)<<endl; 155 int t; 156 cout<<"input t to x\n"; 157 cin>>t; 158 fcantor(x,t,N); 159 for(i=0;i<N;i++) 160 cout<<(int)x[i]; 161 cout<<endl;*/ 162 //cout<<inverse_number(x,N)<<endl; 163 Start=cantor(x,N); 164 End=0; 165 if(inverse_number(x,N)) 166 { 167 bfs(); 168 na=0; 169 while(pre[End].sum!=-1) 170 { 171 ans[na++]=cov[pre[End].d]; 172 End=pre[End].sum; 173 } 174 for(i=na-1;i>=0;i--) 175 cout<<ans[i]; 176 } 177 else 178 cout<<"unsolvable"; 179 cout<<endl; 180 } 181 return 0; 182 }
方法二:
先从终点向起点搜出所有状态,问哪个就输出哪个。
1 //八数码 2 #include<iostream> 3 #include<queue> 4 using namespace std; 5 char x[11]; 6 int factor[]={1,1,2,6,24,120,720,5040,40320,362880,3628800}; 7 bool vis[362881]; 8 int ans; 9 #define N 9 10 int mx[]={0,0,1,-1}; 11 int my[]={1,-1,0,0}; 12 struct 13 { 14 int d; 15 int sum; 16 }pre[362881]; 17 char cov[]={'l','r','u','d'}; //这四个方向都设反了。因为是倒搜 18 int cantor(char *y,int n) /*将全排列转换为一个数*/ 19 { 20 int i,j,cnt,sum=0; 21 for(i=0;i<n;i++) 22 { 23 cnt=0; 24 for(j=i+1;j<n;j++) 25 if(y[j]<y[i]) 26 cnt++; 27 sum+=factor[n-i-1]*cnt; 28 } 29 return sum; 30 } 31 struct node 32 { 33 char G[3][3]; 34 int x,y,ct; 35 }Start; 36 queue<node> Q; 37 int inverse_number(char *y,int m) 38 { 39 int i,j,cnt=0; 40 for(i=1;i<m;i++) 41 { 42 if(y[i]==N)continue; 43 for(j=i-1;j>=0;j--) 44 if(y[j]>y[i]&&y[j]!=N) 45 cnt++; 46 } 47 return !(cnt&1); 48 } 49 void bfs() 50 { 51 memset(vis,0,sizeof(vis)); 52 memset(pre,-1,sizeof(pre)); 53 int i,j,tn,tx,ty; 54 node t; 55 for(i=0;i<3;i++) 56 for(j=0;j<3;j++) 57 t.G[i][j]=i*3+j+1; 58 t.x=2,t.y=2;t.ct=0; 59 while(!Q.empty())Q.pop(); 60 Q.push(t); 61 vis[0]=true; 62 while(!Q.empty()) 63 { 64 t=Q.front();Q.pop(); 65 node p; 66 for(i=0;i<4;i++) 67 { 68 tx=t.x+mx[i]; 69 ty=t.y+my[i]; 70 if(tx<0||ty<0||tx>=3||ty>=3) 71 continue; 72 p=t; 73 p.G[p.x][p.y]=p.G[tx][ty]; 74 p.G[tx][ty]=9; 75 p.x=tx;p.y=ty; 76 tn=cantor(p.G[0],9); 77 p.ct=tn; 78 if(vis[tn])continue; 79 vis[tn]=true; 80 81 Q.push(p); 82 pre[tn].d=i; 83 pre[tn].sum=t.ct; 84 } 85 } 86 } 87 int main() 88 { 89 //freopen("input.txt","r",stdin); 90 int i,j; 91 bfs(); 92 while(true) 93 { 94 for(i=0;i<3;i++) 95 for(j=0;j<3;j++) 96 { 97 if(!(cin>>Start.G[i][j])) 98 return 0; 99 100 if(Start.G[i][j]=='x') 101 { 102 Start.x=i; 103 Start.y=j; 104 Start.G[i][j]=9; 105 } 106 else 107 Start.G[i][j]-='0'; 108 109 } 110 111 112 if(!inverse_number(Start.G[0],9)) 113 { 114 cout<<"unsolvable"<<endl; 115 continue; 116 } 117 ans=cantor(Start.G[0],N); 118 while(ans!=0) 119 { 120 cout<<cov[pre[ans].d]; 121 ans=pre[ans].sum; 122 } 123 cout<<endl; 124 } 125 return 0; 126 }
HDU 1667 The Rotation Game
这个用 ida*
1.估价函数
要保证中间四个数一样,所以统计一下中间 至少 还需移动几次就能让数字相同。因为第移动一次只会 增加或减少一个数。
2.存储状态,基本不用,只要一个数组就行了。dfs返回时把状态改回来。
3.判 重也不用啦。 原因是,如果重复的起很多步肯定比没重复走的用的步数少。
4.用一个数组记录路径
5.预先写好移动的函数。
解决在dfs中 8方向函数调用问题
当然,一个一个写也可以
看看这个吧:void (*pm[4])(int s[N][N],int x);//方便函数批量调用
代码在此:
1 #include<iostream> 2 using namespace std; 3 int G[7][7]; 4 #define N 7 5 int deep; 6 void (*pm[4])(int s[N][N],int x);//方便函数批量调用 7 int mx[]={2,4}; 8 int ans[100000],bs[10000],cs; 9 char opt[4][2]={'A','B','C','D','F','E','H','G'}; 10 11 //**************移动****************************** 12 void up(int s[N][N],int y) 13 { 14 int t=s[0][y]; 15 for(int i=0;i<6;i++) 16 s[i][y]=s[i+1][y]; 17 s[6][y]=t; 18 } 19 void down(int s[N][N],int y) 20 { 21 int t=s[6][y]; 22 for(int i=6;i>0;i--) 23 s[i][y]=s[i-1][y]; 24 s[0][y]=t; 25 } 26 void left(int s[N][N],int x) 27 { 28 int t=s[x][0]; 29 for(int j=0;j<6;j++) 30 s[x][j]=s[x][j+1]; 31 s[x][6]=t; 32 } 33 void right(int s[N][N],int x) 34 { 35 int t=s[x][6]; 36 for(int j=6;j>0;j--) 37 s[x][j]=s[x][j-1]; 38 s[x][0]=t; 39 } 40 //******************移动结束*********************************** 41 42 43 44 //**************估价函数************************************** 45 int check(int s[N][N]) 46 { 47 s[3][3]=0; //把这个设为0,下边就可以用循环统计每个数字出现的个数了 48 int i,j,cnt[4]={0}; 49 for(i=2;i<=4;i++) 50 for(j=2;j<=4;j++) 51 cnt[s[i][j]]++; 52 return max(max(cnt[1],cnt[2]),cnt[3]); //返加了其中出现的最多次 53 } 54 //**************估价函数结束************************************** 55 56 57 //***********dfs***************************** 58 bool dfs(int step) 59 { 60 int h=8-check(G),tj; 61 if(h==0){cs=step;return true;} 62 if(step+h>deep)return false; 63 for(int i=0;i<4;i++) 64 for(int j=0;j<2;j++) 65 { 66 tj=j; 67 if(i>=2)tj=2-j-1; 68 pm[i](G,mx[tj]); 69 ans[step]=i; 70 bs[step]=tj; 71 if(dfs(step+1)) 72 return true; 73 pm[(i+2)%4](G,mx[tj]); 74 } 75 return false; 76 } 77 int main() 78 { 79 //freopen("input.txt","r",stdin); 80 int i,j; 81 while(cin>>G[0][2]&&G[0][2]) 82 { 83 //******************************************************* 84 // input data 85 cin>>G[0][4]; 86 cin>>G[1][2]>>G[1][4]; 87 for(i=0;i<7;i++) 88 cin>>G[2][i]; 89 cin>>G[3][2]>>G[3][4]; 90 for(i=0;i<7;i++) 91 cin>>G[4][i]; 92 cin>>G[5][2]>>G[5][4]>>G[6][2]>>G[6][4]; 93 //***************inpute end******************************** 94 95 96 //*************set point************************************** 97 pm[0]=up; 98 pm[1]=right; 99 pm[2]=down; 100 pm[3]=left; 101 //*********************************************************** 102 103 104 //*****************control dfs********************************** 105 deep=8-check(G); 106 if(deep==0) 107 108 cout<<"No moves needed"<<endl<<G[2][2]<<endl; 109 else 110 { 111 while(!dfs(0))deep++; 112 for(i=0;i<cs;i++) 113 cout<<opt[ans[i]][bs[i]]; 114 cout<<endl<<G[2][2]<<endl; 115 } 116 } 117 return 0; 118 }
HDU 2234 无题I
ida*
1.估价函数
计算每一行至少需要到步数能移成四个一样的数字,并累加起来得到s1
计算每一列至少需要到步数能移成四个一样的数字,并累加起来得到s2
s=min(s1,s2)
返回 (s+3)/4 原因在于 每移动一次能改变四个数字 所以除以4
2。写好四个移动函数,ida*搜索就行了。
代码在此
1 #include<iostream> 2 #include<cstring> 3 #include<stdio.h> 4 #define inf 100000 5 using namespace std; 6 int block[4][4]; 7 int ans; 8 void Left(int (*b)[4],int row) 9 { 10 int t=b[row][0],i; 11 for(i=0;i<3;i++) 12 b[row][i]=b[row][i+1]; 13 b[row][3]=t; 14 } 15 void Right(int (*b)[4],int row) 16 { 17 int t=b[row][3],i; 18 for(i=3;i>0;i--) 19 b[row][i]=b[row][i-1]; 20 b[row][0]=t; 21 } 22 void Up(int (*b)[4],int col) 23 { 24 int i,t=b[0][col]; 25 for(i=0;i<3;i++) 26 b[i][col]=b[i+1][col]; 27 b[3][col]=t; 28 } 29 30 void Down(int (*b)[4],int col) 31 { 32 int i,t=b[3][col]; 33 for(i=3;i>0;i--) 34 b[i][col]=b[i-1][col]; 35 b[0][col]=t; 36 } 37 int h(int (*b)[4]) 38 { 39 int i,j,tmp,ans=inf; 40 int hs[5],cnt; 41 tmp=0; 42 for(i=0;i<4;i++) 43 { 44 cnt=0; 45 memset(hs,0,sizeof(hs)); 46 for(j=0;j<4;j++) 47 hs[b[i][j]]++; 48 for(j=1;j<=4;j++) 49 cnt=max(cnt,hs[j]); 50 51 tmp+=4-cnt; 52 } 53 ans=min(ans,tmp); 54 tmp=0; 55 for(j=0;j<4;j++) 56 { 57 cnt=0; 58 memset(hs,0,sizeof(hs)); 59 for(i=0;i<4;i++) 60 hs[b[i][j]]++; 61 for(i=1;i<=4;i++) 62 cnt=max(cnt,hs[i]); 63 tmp+=4-cnt; 64 } 65 ans=min(ans,tmp); 66 return (ans+3)/4; 67 } 68 69 bool dfs(int step) 70 { 71 int hz=h(block); 72 if(hz==0)return true; 73 if(step+hz>ans) 74 return false; 75 int i; 76 for(i=0;i<4;i++) 77 { 78 Left(block,i); 79 if(dfs(step+1))return true; 80 Right(block,i); 81 } 82 83 for(i=0;i<4;i++) 84 { 85 Right(block,i); 86 if(dfs(step+1))return true; 87 Left(block,i); 88 } 89 90 for(i=0;i<4;i++) 91 { 92 Up(block,i); 93 if(dfs(step+1))return true; 94 Down(block,i); 95 } 96 97 for(i=0;i<4;i++) 98 { 99 Down(block,i); 100 if(dfs(step+1))return true; 101 Up(block,i); 102 } 103 return false; 104 } 105 int main() 106 { 107 //freopen("input.txt","r",stdin); 108 int T,i,j; 109 cin>>T; 110 while(T--) 111 { 112 for(i=0;i<4;i++) 113 for(j=0;j<4;j++) 114 scanf("%d",&block[i][j]); 115 ans=0; 116 while(ans<=5&&!dfs(0)) 117 ans++; 118 if(ans<6) 119 cout<<ans<<endl; 120 else 121 cout<<-1<<endl; 122 } 123 return 0; 124 }
HDU 1560 DNA sequence
继续ida*
1.估价函数
返回 处理到当前状态时,还剩下的最长的字符串的长度。
检举当前的可能的字符x, x={ATCG}四种。 如果对于所用序列,如果开始是x,则开始指针向的移动,如果没有一样的,则说明当前不可能是x,继续下一个。
代码在此:
1 #include<iostream> 2 #include<string.h> 3 #include<stdio.h> 4 using namespace std; 5 char str[10][10],ans[100]; 6 int N,ns[100]; 7 int len[10]; 8 int step; 9 char bz[]="AGCT"; 10 int h() 11 { 12 int i,p=0; 13 for(i=0;i<N;i++) 14 if(len[i]-ns[i]>p) 15 p=len[i]-ns[i]; 16 return p; 17 } 18 bool dfs(int k) 19 { 20 21 int i,j,ht=h(); 22 if(ht==0)return true; 23 if(k+ht>step) 24 return false; 25 int sta[10],top=0; 26 bool flag; 27 for(i=0;i<4;i++) //枚举当前可能的 28 { 29 flag=false; 30 for(j=0;j<N;j++) 31 if(str[j][ns[j]]==bz[i]) 32 { 33 flag=true; 34 ns[j]++; 35 sta[top++]=j; 36 } 37 if(flag) 38 if(dfs(k+1)) 39 return true; 40 for(j=0;j<top;j++) 41 { 42 ns[sta[j]]--; 43 } 44 top=0; 45 } 46 return false; 47 } 48 int main() 49 { 50 //freopen("input.txt","r",stdin); 51 int T,i; 52 cin>>T; 53 while(T--) 54 { 55 cin>>N; 56 for(i=0;i<N;i++) 57 cin>>str[i]; 58 step=0; 59 for(i=0;i<N;i++) 60 len[i]=strlen(str[i]); 61 62 memset(ns,0,sizeof(ns)); 63 while(!dfs(0)) 64 { 65 memset(ns,0,sizeof(ns)); 66 step++; 67 } 68 cout<<step<<endl; 69 } 70 71 return 0; 72 }
HDU 1813 Escape from Tetris
这题一看没思路啊。结果是字典序枚举指令"east","north","south","west"。 总之是 ida*
1. 估价函数:
首先,求出各个点到边上的最小步数。 把边界的空点加入队列进行 bfs。用个数组记录下
然后,所有的空点一起走一步,
在此过程中,返回这些点中到边上的步数中最大的(即上边用数组记录下的。
1 #include<iostream> 2 #include<stdio.h> 3 #include<string.h> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 int N,sp; 8 char G[12][12]; 9 int mx[]={0,-1,1,0}; 10 int my[]={1,0,0,-1}; 11 char outstr[4][6]={"east","north","south","west"}; 12 int ans[100]; 13 struct point 14 { 15 int x,y; 16 bool stop; 17 point(int a=0,int b=0):x(a),y(b){stop=false;} 18 }; 19 vector<point> start; 20 queue<point> Q; 21 bool isedg(int x,int y) 22 { 23 if(x==0||x==N-1||y==0||y==N-1) 24 return true; 25 return false; 26 } 27 int minstep[12][12]; 28 int h() 29 { 30 int maxn=0,n=start.size(),i; 31 for(i=0;i<n;i++) 32 if(maxn<minstep[start[i].x][start[i].y]) 33 maxn=minstep[start[i].x][start[i].y]; 34 return maxn; 35 } 36 bool dfs(int k) 37 { 38 int hz=h(); 39 if(hz==0)return true; 40 if(k+hz>sp)return false; 41 int i,j,n=start.size(),tx,ty; 42 vector<point> tmp(start); 43 bool flag; 44 for(i=0;i<4;i++) 45 { 46 flag=false; 47 for(j=0;j<n;j++) 48 if(!start[j].stop) 49 { 50 flag=true; 51 tx=start[j].x+mx[i]; 52 ty=start[j].y+my[i]; 53 if(G[tx][ty]=='1') 54 continue; 55 if(isedg(tx,ty)) //判断是不是到了边上 56 start[j].stop=true; 57 start[j].x=tx; 58 start[j].y=ty; 59 } 60 if(!flag) 61 return true; 62 else 63 { 64 ans[k]=i; 65 if(dfs(k+1)) 66 return true; 67 } 68 for(int var=0;var<n;var++) 69 start[var]=tmp[var]; 70 } 71 return false; 72 } 73 void bfs() //广搜出各个点最少能到边上的距离 74 { 75 int i,tx,ty; 76 point t; 77 while(!Q.empty()) 78 { 79 t=Q.front();Q.pop(); 80 for(i=0;i<4;i++) 81 { 82 tx=t.x+mx[i]; 83 ty=t.y+my[i]; 84 if(tx>=0&&tx<N&&ty>=0&&ty<N) 85 if(G[tx][ty]=='0') 86 if(minstep[tx][ty]>minstep[t.x][t.y]+1) 87 { 88 minstep[tx][ty]=minstep[t.x][t.y]+1; 89 Q.push(point(tx,ty)); 90 } 91 } 92 } 93 94 } 95 int main() 96 { 97 //freopen("input.txt","r",stdin); 98 int i,j; 99 bool isfirst=true; 100 while(cin>>N) 101 { 102 start.clear(); 103 while(!Q.empty())Q.pop(); 104 memset(minstep,127,sizeof(minstep)); 105 for(i=0;i<N;i++) 106 cin>>G[i]; 107 for(i=0;i<N;i++) 108 for(j=0;j<N;j++) 109 if(G[i][j]=='0') 110 if(!isedg(i,j)) 111 start.push_back(point(i,j)); //将中间的空点装入数组,以备深搜 112 else 113 { 114 minstep[i][j]=0; //将边界空点装入队列,以备广搜 115 Q.push(point(i,j)); 116 } 117 bfs(); 118 sp=0; 119 while(!dfs(0)) 120 sp++; 121 if(!isfirst) 122 cout<<endl; 123 isfirst=false; 124 for(j=0;j<sp;j++) 125 cout<<outstr[ans[j]]<<endl; 126 } 127 return 0; 128 }
HDU 2918 Tobo or not Tobo
这个还是ida*
1.估价函数:
(曼哈顿距离+3)/4 除以4是因为,每转一次会改变四方块,也就是曼哈顿距离会变4
然后我写了顺时针转和逆时针转的函数。
又把四旋钮的所控制的四个方块,分别存到数组里
ida* 搜索吧
代码在此:
1 #include<iostream> 2 #include<stdio.h> 3 using namespace std; 4 int board[10][10]; 5 int lim; 6 int *p[4][4]; 7 int ans; 8 void (*rot[2])(int *p[]); 9 void AntiClockWise(int *p[]) 10 { 11 int t=*p[0],i; 12 for(i=0;i<3;i++) 13 *p[i]=*p[i+1]; 14 *p[3]=t; 15 } 16 void ClockWise(int *p[]) 17 { 18 int t=*p[3],i; 19 for(i=3;i>0;i--) 20 *p[i]=*p[i-1]; 21 *p[0]=t; 22 } 23 void set_point() 24 { 25 rot[0]=ClockWise; 26 rot[1]=AntiClockWise; 27 p[0][0]=&board[0][0]; p[0][1]=&board[0][1]; p[0][2]=&board[1][1]; p[0][3]=&board[1][0]; 28 p[1][0]=&board[0][1]; p[1][1]=&board[0][2]; p[1][2]=&board[1][2]; p[1][3]=&board[1][1]; 29 p[2][0]=&board[1][0]; p[2][1]=&board[1][1]; p[2][2]=&board[2][1]; p[2][3]=&board[2][0]; 30 p[3][0]=&board[1][1]; p[3][1]=&board[1][2]; p[3][2]=&board[2][2]; p[3][3]=&board[2][1]; 31 } 32 void show_state() 33 { 34 int i,j; 35 for(i=0;i<3;i++) 36 { 37 for(j=0;j<3;j++) 38 cout<<board[i][j]; 39 cout<<endl; 40 } 41 cout<<endl; 42 } 43 int h() 44 { 45 int i,j,tx,ty,s=0; 46 for(i=0;i<3;i++) 47 for(j=0;j<3;j++) 48 { 49 tx=(board[i][j]-1)/3; 50 ty=(board[i][j]-1)%3; 51 s+=abs(i-tx)+abs(j-ty); 52 } 53 return (s+3)/4; 54 } 55 bool dfs(int deep) 56 { 57 int i,j,d=h(); 58 if(d==0)return true; 59 if(d+deep>ans)return false; 60 for(i=0;i<4;i++) 61 for(j=0;j<2;j++) 62 { 63 rot[j](p[i]); 64 if(dfs(deep+1))return true; 65 rot[j^1](p[i]); 66 } 67 return false; 68 } 69 int main() 70 { 71 //freopen("input.txt","r",stdin); 72 int i,j,sum,cs=0; 73 set_point(); 74 while(scanf("%1d",&lim)) 75 { 76 cs++; 77 sum=0; 78 for(i=0;i<3;i++) 79 for(j=0;j<3;j++) 80 { 81 scanf("%1d",&board[i][j]); 82 sum+=board[i][j]; 83 } 84 sum+=lim; 85 if(sum==0)break; 86 //show_state(); 87 ans=0; 88 while(ans<=lim&&!dfs(0)) 89 ans++; 90 if(ans>lim) 91 printf("%d. %d\n",cs,-1); 92 else 93 printf("%d. %d\n",cs,ans); 94 } 95 return 0; 96 }
HDU 3459 Rubik 2×2×2
二阶段魔方问题。 ida*
估价函数:
在原点的那个方块是不动的,所心左,下,后三个面的颜色是确定的。 h()函数
先统计 这三个面上各自不应该出现的颜色的数量,并累加起来得到s。然后返回(s+3)/4 因为第次旋转会改变这三个面上最多4块的面积。
我写的这个暴力代码,基本是不用循环的。 希望能找到一定的规律以减少代码量。
代码在此:
1 #include<iostream> 2 #include<stdio.h> 3 using namespace std; 4 char G[6][9]; 5 int ans; 6 char opt[4]="XYZ"; 7 char sto[200]; 8 void(*p[3])(); 9 void x_turn() 10 { 11 char a,b; 12 a=G[4][3]; 13 b=G[5][3]; 14 G[5][3]=G[3][3]; G[4][3]=G[2][3]; 15 G[3][3]=G[1][3]; G[2][3]=G[0][3]; 16 G[1][3]=G[2][6]; G[0][3]=G[3][6]; 17 G[2][6]=b; G[3][6]=a; 18 char t=G[2][4]; 19 G[2][4]=G[2][5]; G[2][5]=G[3][5]; 20 G[3][5]=G[3][4]; G[3][4]=t; 21 } 22 void y_turn() 23 { 24 char a=G[2][6],b=G[2][7]; 25 int i; 26 for(i=7;i>1;i-=2) 27 { 28 G[2][i]=G[2][i-2]; 29 G[2][i-1]=G[2][i-3]; 30 } 31 G[2][1]=b; G[2][0]=a; 32 char t=G[0][2]; 33 G[0][2]=G[0][3]; G[0][3]=G[1][3]; 34 G[1][3]=G[1][2]; G[1][2]=t; 35 } 36 void z_turn() 37 { 38 char a=G[1][2],b=G[1][3]; 39 G[1][2]=G[2][4]; G[1][3]=G[3][4]; 40 G[2][4]=G[4][3]; G[3][4]=G[4][2]; 41 G[4][3]=G[3][1]; G[4][2]=G[2][1]; 42 G[3][1]=a; G[2][1]=b; 43 char t=G[2][2]; 44 G[2][2]=G[2][3]; G[2][3]=G[3][3]; 45 G[3][3]=G[3][2]; G[3][2]=t; 46 } 47 void set_point() 48 { 49 p[0]=x_turn; 50 p[1]=y_turn; 51 p[2]=z_turn; 52 } 53 int h() 54 { 55 int sum=0; 56 char a; 57 a=G[3][0]; 58 if(G[3][1]!=a)sum++; if(G[2][0]!=a)sum++; if(G[2][1]!=a)sum++; 59 a=G[5][2]; 60 if(G[5][3]!=a)sum++; if(G[4][2]!=a)sum++; if(G[4][3]!=a)sum++; 61 a=G[3][7]; 62 if(G[3][6]!=a)sum++; if(G[2][6]!=a)sum++; if(G[2][7]!=a)sum++; 63 return (sum+3)/4; 64 } 65 bool dfs(int deep) 66 { 67 int i; 68 int hz=h(); 69 if(hz==0)return true; 70 if(deep+h()>ans)return false; 71 char tmp[6][9]; 72 memcpy(tmp,G,sizeof(G)); 73 for(i=0;i<3;i++) 74 { 75 p[i](); 76 sto[deep]=i; 77 if(dfs(deep+1)) 78 return true; 79 memcpy(G,tmp,sizeof(tmp)); 80 } 81 return false; 82 } 83 int main() 84 { 85 //freopen("input.txt","r",stdin); 86 int i; 87 bool flag; 88 char ps[6][9]; 89 90 set_point(); 91 while(true) 92 { 93 flag=false; 94 for(i=0;i<6;i++) 95 cin>>G[i]; 96 if(G[2][2]=='.') 97 break; 98 memcpy(ps,G,sizeof(G)); 99 ans=0; 100 while(!dfs(0))ans++; 101 memcpy(G,ps,sizeof(G)); 102 for(i=0;i<ans;i++) 103 putchar(opt[sto[i]]); 104 putchar('\n'); 105 } 106 return 0; 107 }
ida*也就这些了。
ida* A* dfs比较
A*算法 框架:
估价函数:
状态存储:
判重:
就是bfs了。
记录路径
A* 是要用优先队列了。STL
ida*:
估价函数h();
bool dfs(deep)
{
int hz=h();
if(hz==0)return true;
if(hz+deep>ans)return false;
计算 下一个状态。。if(dfs(deep+1))return true;
恢复环境。
}
int main()
{
读入数据;
ans=0;
while(!dfs(0)) ans++;
cout<<ans<<endl;
}
ida*与A*相比,不用记录那么多状态,也不用判重。 如果问题的状态很多那就不能A*了
ida* 重复搜索很多。
ida*与普通dfs相比:
有效的控制了深度,避免在一个方向上过度搜索。
于是,在问题的状态很多,而且深度可控制时可以用ida*
估价函数的设计:
算曼哈顿距离,还很常用。然后就尽量挖题里的限制吧。 有时个看似不起眼的估价,作用很大的。
状态存储和判重:
康托。 hash set map
STL的效率不怎么样啊。
注意代码不要出现中文空格啊,上次一不小心……