【NOIP2013提高组】华容道

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
#include
#include
#include
#include
#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(headif(now==3601){
            int pop=1;
        }
        fo(i,1,map[now][0]){
            if(d[now]+chang[now][map[now][i]]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(head0,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;
            }
        }
    }
}

你可能感兴趣的:(noip,DP,最短路)