骑士移动问题,计算从所给状态到目标状态需要多少步。
此题数据不是很强,我偷了一下懒。。。我直接用的vis数组是否访问过,由于数组大小为500000000,每次memset会超时,就试着把memset的大小改成200000000交了上去,本不报希望的,结果过了。。。
其实比较好的方法是用哈希表判重,但我看到了另一种方法,只标记上次的状态,bfs时只避免访问上次的位置,还可以通过计算不相同的位置的个数来剪枝,这样速度很快。。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[30000000][5][5],input[5][5],des[5][5]={{'1','1','1','1','1'},{'0','1','1','1','1'},{'0','0',' ','1','1'},{'0','0','0','0','1'},{'0','0','0','0','0'},};
int vector[8][2]={{-1,-2},{-2,-1},{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},};
char ss[30000000],vis[20000000][25];
int a,b;
void getvalue(int x,int y,char p[5][5])
{
int i,j,flag=0;
a=0;
for(i=0;i<5;i++)
{
for(j=0;j<5;j++)
{
if(i==x&&j==y)
{
flag=1;
continue;
}
a+=(p[i][j]-'0')*(1<<(i*5+j-flag));
}
}
b=x*5+y;
}
void bfs()
{
int front=0,rear=1,i,j,t,x,y;
for(i=0;i<5;i++)
{
for(j=0;j<5;j++)
s[front][i][j]=input[i][j];
}
ss[front]=0;
while(front<rear)
{
if(ss[front]>10)
{
printf("Unsolvable in less than 11 move(s).\n");
break;
}
if(memcmp(s[front],des,25)==0)
{
printf("Solvable in %d move(s).\n",ss[front]);
break;
}
for(x=0;x<5;x++)
{
for(y=0;y<5;y++)
{
if(s[front][x][y]==' ')
goto next;
}
}
next:{}
for(i=0;i<8;i++)
{
if(x+vector[i][0]>=0&&x+vector[i][0]<5&&y+vector[i][1]>=0&&y+vector[i][1]<5)
{
for(j=0;j<5;j++)
{
for(t=0;t<5;t++)
s[rear][j][t]=s[front][j][t];
}
s[rear][x][y]=s[front][x+vector[i][0]][y+vector[i][1]];
s[rear][x+vector[i][0]][y+vector[i][1]]=' ';
ss[rear]=ss[front]+1;
getvalue(x+vector[i][0],y+vector[i][1],s[rear]);
if(!vis[a][b])
{
rear++;
vis[a][b]=1;
}
}
}
front++;
}
}
int main()
{
int n,i,j,all;
scanf("%d",&n);
getchar();
while(n--)
{
memset(input,0,25);
memset(vis,0,20000000);
all=0;
for(i=0;i<5;i++)
{
for(j=0;j<5;j++)
{
scanf("%c",&input[i][j]);
}
getchar();
}
bfs();
}
return 0;
}
附:高速代码(来自http://blog.csdn.net/frankiller/article/details/7839423)
#include<stdio.h>
#include<string.h>
int a[5][5],move[8][2]={{-1,-2},{-1,2},{-2,1},{-2,-1},{2,1},{2,-1},{1,-2},{1,2}},
b[5][5]={{1,1,1,1,1},{0,1,1,1,1},{0,0,2,1,1},{0,0,0,0,1},{0,0,0,0,0}},min;
int dfs(int x,int y,int step,int way)
{int i,j,X,Y,sum=0,t;
if (step>=min) return 0;
for (i=0;i<5;i++)
for (j=0;j<5;j++)
if (a[i][j]!=b[i][j]) ++sum;
if (sum==0) {if (step<min) min=step; return 0;}
if ((sum+1)/2+step>=min) return 0; //SUM表示与目标状态不同的位置个数,则最少需要(移动一次最多2个位置与目标状态相同)sum/2+sum%2=(sum+1)/2步才能变为目标状态
for (i=0;i<8;i++)
{X=x+move[i][0];
Y=y+move[i][1];
if ((X>=0)&&(X<5)&&(Y>=0)&&(Y<5)&&(way+i!=7)) //初始化move时将相反跳法的下标和为7,这样就可以每次避免走回头路省略判重过程
{t=a[x][y];a[x][y]=a[X][Y];a[X][Y]=t;
dfs(X,Y,step+1,i);
t=a[x][y];a[x][y]=a[X][Y];a[X][Y]=t;
}
}
}
int main()
{int i,j,x,y,n;
char s[6];
scanf("%d\n",&n);
while (n--)
{
for (i=0;i<5;i++)
{gets(s);
for (j=0;s[j]!='\0';j++)
if (s[j]==' ') {a[i][j]=2;x=i;y=j;}
else a[i][j]=s[j]-'0';
}
min=11;
dfs(x,y,0,8);
if (min==11) printf("Unsolvable in less than 11 move(s).\n");
else printf("Solvable in %d move(s).\n",min);
}
return 0;
}