题目链接: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;
}
posted on 2017-02-10 15:32  nhc2014  阅读(480)  评论(0编辑  收藏  举报