Description
小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间。
小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:
在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的;
有些棋子是固定的,有些棋子则是可以移动的;
任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。 游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。
给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候,空白的格子在第 EX_i 行第 EY_i 列,指定的可移动棋子的初始位置为第 SX_i 行第 SY_i 列,目标位置为第 TX_i 行第 TY_i 列。
假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。
Solution
一看就可以bfs暴搜
可惜只用60分。
发现移动的棋子转移依赖于白格
每次白格都必须在移动棋子的旁边,棋子才能移动。
最短路算法!
每次把棋子移动到格子的旁边,明显是用最短路。
我们改一下思路,设一个DP
因为转移只跟格子的坐标和白格在上下左右的状态有关,设f[i,j,k]表示格子坐标为(i,j),白格在格子的方向k(0,1,2,3)。那么这个状态就可以转到别的状态了,转移很明显,打这个明显比bfs容易,但是还是很慢。
用最短路算法优化DP
发现状态到状态之间的转移的步数是一定的,那么可以预处理出来f[i,j,k]到f[i,j,l]或者白格与格子交换,这个需要预处理出来。
那么下面状态之间的转移打一个spfa就好了。
优化一下常数
设一个初始状态S和目标状态T,最短路每次只用从S走向T就好了。
注意!
因为S是向询问一开始的状态连边,结束状态向T连边,那么询问完之后需要删边!
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int i,j,k,l,t,n,m,ans,ex,ey,sx,sy,tx,ty,x,y,xx,yy,qq;
int chang[4005][4005];
int fang[4][2]={-1,0,1,0,0,1,0,-1};
bool cz[50][50],ui[50000];
int head,tail,now;
int a[50][50];
int d[100000];
int map[50000][10],num,q[50000][3];
int de(int x,int y,int z){return z*n*m+(x-1)*m+y;}
void add(int x,int y,int z){map[x][++map[x][0]]=y;chang[x][y]=z;}
int spfa(){
int head=0,tail=1,now,data[30000];
bool bz[30000];
memset(data,0,sizeof(data));
memset(d,127,sizeof(d));
int opp=d[0];
d[0]=0;
data[1]=0;
memset(bz,0,sizeof(bz));
bz[0]=1;
while(head<tail){
now=data[++head];
if(now==3601){
int pop=1;
}
fo(i,1,map[now][0]){
if(d[now]+chang[now][map[now][i]]<d[map[now][i]]){
if(map[now][i]==3601){
int pop=1;
}
d[map[now][i]]=d[now]+chang[now][map[now][i]];
if(!bz[map[now][i]]){
bz[map[now][i]]=1;
data[++tail]=map[now][i];
}
}
}
bz[now]=0;
}
if(d[4*n*m+1]==opp)return -1;else return d[4*n*m+1];
}
int bfs(int x,int y,int xx,int yy,int u,int v){
memset(q,0,sizeof(q));
memset(cz,0,sizeof(cz));
int head=0,tail=1,now,i,yi,er,oi=0x7fffffff;
q[1][1]=x;q[1][2]=y;
cz[x][y]=1;
if(x==xx&&y==yy)return 0;
while(head<tail){++head;
fo(i,0,3){
yi=fang[i][0]+q[head][1];er=fang[i][1]+q[head][2];
if(yi<1||yi>n||er<1||er>m||a[yi][er]==0||cz[yi][er])continue;
if(yi==u&&er==v)continue;
if(yi==xx&&er==yy){
return q[head][0]+1;
}
q[++tail][1]=yi;q[tail][2]=er;cz[yi][er]=1;
q[tail][0]=q[head][0]+1;
}
}
return -1;
}
int main(){
scanf("%d%d%d",&n,&m,&qq);
fo(i,1,n){
fo(j,1,m){
scanf("%d",&a[i][j]);
}
}
fo(i,1,n){
fo(j,1,m){
if(a[i][j]==0)continue;
fo(k,0,3){
x=i+fang[k][0];y=j+fang[k][1];
if(x<1||x>n||y<1||y>m||a[x][y]==0)continue;
int dd;
if (k%2) dd=k-1;else dd=k+1;
add(de(i,j,k),de(x,y,dd),1);
fo(l,0,3){
xx=i+fang[l][0];yy=j+fang[l][1];
if(l==k)continue;
if(xx<1||xx>n||yy<1||yy>m||a[xx][yy]==0)continue;
t=bfs(x,y,xx,yy,i,j);
if(t!=-1)add(de(i,j,k),de(i,j,l),t);
}
}
}
}
fo(j,1,qq){
scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
if(sx==tx&&sy==ty){
printf("0\n");
continue;
}
if(j==64){
ui[4]=1;
}
fo(k,0,3){
x=sx+fang[k][0];y=sy+fang[k][1];
if(x<1||x>n||y<1||y>m||a[x][y]==0)continue;
t=bfs(ex,ey,x,y,sx,sy);
if(t!=-1)add(0,de(sx,sy,k),t);
}
fo(k,0,3){
x=tx+fang[k][0];y=ty+fang[k][1];
if(x<1||x>n||y<1||y>m||a[x][y]==0)continue;
add(de(tx,ty,k),4*n*m+1,0);
ui[k]=1;
}
ans=spfa();
printf("%d\n",ans);
if(ans==14149){
ui[4]=1;
}
map[0][0]=0;
fo(k,0,3){
if(ui[k]){
ui[k]=0;
map[de(tx,ty,k)][0]--;
chang[de(tx,ty,k)][4*n*m+1]=0;
}
}
}
}