[noip2013]华容道(bfs+spfa)

来源:互联网 发布:哈登第四周场均数据 编辑:程序博客网 时间:2024/05/14 09:19

题目:

我是超链接

题解:

这种想着办法优化的题。。。
70pts:暴力——把空白格子随意乱移,遇到目标格子就换个位置,bfs就ok
100pts:bfs+spfa

很明确的是要想把格子S移到目标位置T,首先得把白格子E移到S的旁边,在通过一系列的操作使格子S到达T

格子S想要走到与它相邻的k方向(left,right,up,down)上的格子,必须先把E格子通过w的花费移到那里,然后在用1的花费使S走到那 里,总花费为w+1,由于从一个格子向相邻方向上走一步的花费要多次使用,因此我们把它预处理成数组move[x][y][k][h],表示这个棋子(x,y),白格子在k方向上,使棋子走到h方向上相邻的格子的最少花费,这个可以用BFS预处理

有了这么多东西,能不能用BFS做呢?不行。从一个点走到相邻的节点的花费不一定是1,而且还受白格子所在方向的影响,所以就不再设状态为(x,y),而是(x,y,k),k表示白格子的方向,dist[x][y][k]存储走到这个状态的最小步数。很明显,从一个格子向h方向走的最小代价就是move[x][y][k][h],很明显这个状态只能转移到(x,y,other(h)),other(h)表示和h相反的方向,比如你向右走完一次,白格子一定在这个格子的左侧。

现在已经构建了一个又状态(x,y,k)构成的一张图,边权也已经算出来了,因此想要从出发点找一条到结束点的最短路径,可以用SPFA

move[i][j][k][l]表示棋子在i,j时,白格子在ta的k方向上,使棋子走到l方向的最少花费
dis[i][j][k]表示棋子在i,j白格子在k方向的最小花费

代码:

70pts

#include <cstdio>#include <queue>#include <cstring>using namespace std;int EXi,EYi,SXi,SYi,TXi,TYi,n,m;bool a[35][35],vis[35][35][35][35];struct hh{int x,y,a,b,bs;};const int c[4][2]={{1,0},{0,1},{-1,0},{0,-1}};int bfs(){    memset(vis,0,sizeof(vis));    queue<hh>q;    q.push((hh){EXi,EYi,SXi,SYi,0});    vis[EXi][EYi][SXi][SYi]=1;    while (!q.empty())    {        hh now=q.front(); q.pop();        if (now.a==TXi && now.b==TYi) return now.bs;        for (int i=0;i<=3;i++)        {            int xx=now.x+c[i][0],yy=now.y+c[i][1],aa=now.a,bb=now.b;            if (xx<1 || yy<1 || xx>n || yy>m || !a[xx][yy]) continue;            if (xx==now.a && yy==now.b){aa=now.x; bb=now.y;}            if (vis[xx][yy][aa][bb]) continue;            vis[xx][yy][aa][bb]=1;            q.push((hh){xx,yy,aa,bb,now.bs+1});        }    }    return -1;}int main(){    int i,q,j;    scanf("%d%d%d",&n,&m,&q);    for (i=1;i<=n;i++)      for (j=1;j<=m;j++)        scanf("%d",&a[i][j]);    while (q--)    {        scanf("%d%d%d%d%d%d",&EXi,&EYi,&SXi,&SYi,&TXi,&TYi);        if (SXi==TXi && SYi==TYi) {printf("0\n");continue;}        printf("%d\n",bfs());                       }}

100pts

#include <cstdio>#include <queue>#include <cstring>#define up 1#define down 2#define left 3#define right 4#define INF 1e9using namespace std;struct point{int x,y;};struct hh{int x,y,k;};int EXi,EYi,SXi,SYi,TXi,TYi,n,m,deep[35][35],move[35][35][5][5],dis[35][35][5];const int other[5]={0,2,1,4,3};bool a[35][35],viv[35][35],vis[35][35][5];point where(point x,int fx){    if (fx==up) x.x--;    if (fx==down) x.x++;    if (fx==left) x.y--;    if (fx==right) x.y++;    return x;}int bfs(point x,point y){    memset(deep,0x7f,sizeof(deep));    memset(viv,0,sizeof(viv));    queue<point>q;    q.push(x);    deep[x.x][x.y]=0;    viv[x.x][x.y]=1;    point now;    while (!q.empty())    {        now=q.front(); q.pop();        if (now.x==y.x && now.y==y.y) break;        for (int i=1;i<=4;i++)        {            point wo=where(now,i);            int xx=wo.x,yy=wo.y;            if (xx<1 || yy<1 || xx>n || yy>m || !a[xx][yy]) continue;            if (viv[xx][yy]) continue;            viv[xx][yy]=1;deep[xx][yy]=deep[now.x][now.y]+1;            q.push((point){xx,yy});        }    }    return deep[y.x][y.y];}void init()//move[i][j][k][l]表示棋子在i,j时,白格子在ta的k方向上,使棋子走到l方向的最少花费 {    int i,j,k,l;    for (i=1;i<=n;i++)      for (j=1;j<=m;j++)        if (a[i][j])        {          a[i][j]=0;//这个目标格子的位置不能经过           for (k=1;k<=4;k++)            for (l=1;l<=4;l++)            {                if (l<k) {move[i][j][k][l]=move[i][j][l][k]; continue;}                point t1=where((point){i,j},k),t2=where((point){i,j},l);                if (!a[t1.x][t1.y] || !a[t2.x][t2.y]) continue;                move[i][j][k][l]=bfs(t1,t2)+1;            }          a[i][j]=1;        }}int spfa(point x,point y)//dis[i][j][k]表示棋子在i,j白格子在k方向的最小花费 {    memset(vis,0,sizeof(vis));    memset(dis,0x7f,sizeof(dis));    queue<hh>q;    a[y.x][y.y]=0;    for (int i=1;i<=4;i++)     {        int lj=bfs(x,where(y,i));        if (lj<INF) dis[y.x][y.y][i]=lj,q.push((hh){y.x,y.y,i}),vis[y.x][y.y][i]=1;     }    a[y.x][y.y]=1;    while (!q.empty())    {        hh now=q.front(); q.pop(); vis[now.x][now.y][now.k]=0;        for (int i=1;i<=4;i++)        {            point to=where((point){now.x,now.y},i);            if (to.x<1 || to.y<1 || to.x>n || to.y>m || !a[to.x][to.y]) continue;            if (dis[now.x][now.y][now.k]+move[now.x][now.y][now.k][i]<dis[to.x][to.y][other[i]])            {                dis[to.x][to.y][other[i]]=dis[now.x][now.y][now.k]+move[now.x][now.y][now.k][i];                if (!vis[to.x][to.y][other[i]]) vis[to.x][to.y][other[i]]=1,q.push((hh){to.x,to.y,other[i]});                       }        }    }    int ans=INF+50;    for (int i=1;i<=4;i++) ans=min(ans,dis[TXi][TYi][i]);    if (ans>INF) return -1;    return ans;}int main(){    int i,q,j;    scanf("%d%d%d",&n,&m,&q);    for (i=1;i<=n;i++)      for (j=1;j<=m;j++)        scanf("%d",&a[i][j]);    init();    while (q--)    {        scanf("%d%d%d%d%d%d",&EXi,&EYi,&SXi,&SYi,&TXi,&TYi);        if (SXi==TXi && SYi==TYi) {printf("0\n");continue;}        printf("%d\n",spfa((point){EXi,EYi},(point){SXi,SYi}));                       }}

后方吐槽:这正解好烦啊啊啊啊啊啊

原创粉丝点击