题意:
给出原始阵列
0
1 1
2 2 2
3 3 3 3
4 4 4 4 4
5 5 5 5 5 5
问对于给出的某种阵列,能否通过每步只移动 0 至(i - 1 , j)or ( i - 1 , j - 1) or ( i + 1 , j ) or ( i + 1 , j + 1 ) ,在20步之内移动到原始阵列。
思路:
考虑到只要求20步,直接搜索即可。
但 4 ^ 20 一定会 TLE 。考虑剪枝
显然的剪枝是,对于任意局面。
当且仅当 剩余步数 + 1 >= 非法位置的个数 时,有可行解
交上去仍然TLE
考虑到没有状态去重
但由于移动步伐诡异,剪去原地摩擦的情况就能得到很好的效果。
故对父节点做了访问去重。
貌似是数据较水的原因15MS水过。
正解是双向BFS O(4^10 * hash_status)
代码:
#include <bits/stdc++.h>
using namespace std;
int a[10][10],T,stx,sty;
int dirx[]={-1,-1,1,1};
int diry[]={-1,0,0,1};
int ans;
void dfs(int x,int y,int step,int left,int fax,int fay){
if(step>=ans||(ans-step)+1<left) return ;//剪枝1
if(left==0){
ans=step;
return ;
}
for(int i=0;i<4;i++){
int xx=x+dirx[i],yy=y+diry[i];
if(xx>=1&&xx<=6&&yy>=1&&yy<=xx){
if(xx==fax&&yy==fay) continue;//剪枝2 (原地摩擦的情况)
int shift=0;
int num=a[xx][yy];
if(num==xx) shift++;
else if(x==num) shift--;
if(x==1) shift++;
else if(xx==1) shift--;
swap(a[x][y],a[xx][yy]);
dfs(xx,yy,step+1,left+shift,x,y);
swap(a[x][y],a[xx][yy]);
}
}
return ;
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d",&T);
while(T--){
int left=0;
for(int i=1;i<=6;i++){
for(int j=1;j<=i;j++){
scanf("%d",&a[i][j]);a[i][j]++;
if(a[i][j]==1){
stx=i;sty=j;
}
if(a[i][j]!=i) left++;
}
}
ans=21;
dfs(stx,sty,0,left,0,0);
if(ans==21){
puts("too difficult");
}else
printf("%d\n",ans);
}
}