题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4761
这道题我看了半天别人的题解,为了很久以后我还能看懂,所以写题解来记录一下。
此题为usaco20171月月赛金组
这道题是一道动归加上spfa,用dis[x1][y1][x2][y2][d1][d2]来表示两条路分别在x1,y1,面向d1,和在x2,y2,面向d2,从起点出发要到这个状态所需的最短指令。
spfa中,分三种情况来讨论三种做法,最后的得到结果。
具体的解释和意思在代码注释里。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int n,m[100][100],dis[30][30][30][30][5][5]; bool c[30][30][30][30][5][5]; int nowx1,nowx2,nowy1,nowy2; int h[]={0,-1,0,1,0},l[]={0,0,1,0,-1}; int sum1,ans,sum2,td1,td2; char a[100][100]; struct gather{ int x1,y1,x2,y2,d1,d2; gather (int x1,int y1,int x2,int y2,int d1,int d2): x1(x1),y1(y1),x2(x2),y2(y2),d1(d1),d2(d2){} gather () {} }; //上面这段定义,是我无耻地抄了一个博客,因为我完全不知道该怎么把那六个元素定义起来....也是看了那个人的博客后才指导方法的。。。 我们就姑且算作是定义了一个包含六个元素的集合吧。。。 queue<gather>q;//定义一个队列 bool check(int x,int y) { if ((x<1)||(x>n)||(y<1)||(y>n)) return false; else return true; }//检查目前的这个坐标有没有越界 void spfa() { q.push(gather(n,1,n,1,1,2));//把第一个集合放进去,也就是贝西的起始位置 c[n][1][n][1][1][2]=true;//这个集合已经出队列了 while (!q.empty())//如果集合不为空 { gather p=q.front();//拿出队首的集合 q.pop();//删队首集合 //1.第一种方案,按当前方向继续向前走 nowx1=p.x1+h[p.d1]; nowy1=p.y1+l[p.d1]; nowx2=p.x2+h[p.d2]; nowy2=p.y2+l[p.d2]; //以上是两条路在继续按当前方向走后,会到达的点 if ((check(nowx1,nowy1)==false)||(m[nowx1][nowy1]==false)){ nowx1=p.x1; nowy1=p.y1; } if ((check(nowx2,nowy2)==false)||(m[nowx2][nowy2]==false)){ nowx2=p.x2; nowy2=p.y2; } //以上两个if语句表示如果继续向前走一步,到达的点如果超出图的范围,或者是那个店存在石头,都是不能走的,应停留在原来的位置。 if ((p.x1==1)&&(p.y1==n)){nowx1=1;nowy1=n;} if ((p.x2==1)&&(p.y2==n)){nowx2=1;nowy2=n;} //这个点表示如果其中任意一条路原本已经到了终点,就不用再走了,停在原来位置。 sum1=dis[nowx1][nowy1][nowx2][nowy2][p.d1][p.d2]; //目前为止向前走需要的最短指令 sum2=dis[p.x1][p.y1][p.x2][p.y2][p.d1][p.d2]; //走到原来位置需要的最短指令 if (sum1>sum2+1)//更新最短指令 { dis[nowx1][nowy1][nowx2][nowy2][p.d1][p.d2]=sum2+1; if (c[nowx1][nowy1][nowx2][nowy2][p.d1][p.d2]==false) q.push(gather(nowx1,nowy1,nowx2,nowy2,p.d1,p.d2)); //如果不在队列内,就放进去 c[nowx1][nowy1][nowx2][nowy2][p.d1][p.d2]=true; } //2.向右转90度 td1=p.d1+1; td2=p.d2+1; if (td1>4) td1=1; if (td2>4) td2=1; //更新所指向的方向 sum1=dis[p.x1][p.y1][p.x2][p.y2][td1][td2]; if (sum1>sum2+1)//更新最短指令 { dis[p.x1][p.y1][p.x2][p.y2][td1][td2]=sum2+1; if (c[p.x1][p.y1][p.x2][p.y2][td1][td2]==false) q.push(gather(p.x1,p.y1,p.x2,p.y2,td1,td2)); c[p.x1][p.y1][p.x2][p.y2][td1][td2]=true; } //3.向左转90度 td1=p.d1-1; td2=p.d2-1; if (td1<1) td1=4; if (td2<1) td2=4; sum1=dis[p.x1][p.y1][p.x2][p.y2][td1][td2]; if (sum1>sum2+1) { dis[p.x1][p.y1][p.x2][p.y2][td1][td2]=sum2+1; if (c[p.x1][p.y1][p.x2][p.y2][td1][td2]==false) q.push(gather(p.x1,p.y1,p.x2,p.y2,td1,td2)); c[p.x1][p.y1][p.x2][p.y2][td1][td2]=true; } c[p.x1][p.y1][p.x2][p.y2][p.d1][p.d2]=false; //出队列 } } int main() { scanf("%d",&n); a[1][1]=getchar(); for (int i=1;i<=n;i++) for (int j=1;j<=n+1;j++) { a[i][j]=getchar(); if (a[i][j]=='E') m[i][j]=true; else if (a[i][j]=='H') m[i][j]=false; } //我们用dis[x1][y1][x2][y2][d1][d2]来表示两条路分别在x1,y1,面向d1, 和在x2,y2,面向d2,从起点出发要到这个状态所需的最短指令。 for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) for (int i1=1;i1<=n;i1++) for (int j1=1;j1<=n;j1++) for (int u=1;u<=4;u++) for (int d=1;d<=4;d++) dis[i][j][i1][j1][u][d]=2147483647/2;//初始化 dis[n][1][n][1][1][2]=0; ans=2147483647/2; spfa(); for (int i=1;i<=4;i++) for (int j=1;j<=4;j++) ans=min(ans,dis[1][n][1][n][i][j]); cout<<ans<<endl; }