USACO17JAN Cow Navigation 奶牛导航

题目描述

Bessie has gotten herself stuck on the wrong side of Farmer John's barn again, and since her vision is so poor, she needs your help navigating across the barn.

The barn is described by an  grid of square cells (), some being empty and some containing impassable haybales. Bessie starts in the lower-left corner (cell 1,1) and wants to move to the upper-right corner (cell ). You can guide her by telling her a sequence of instructions, each of which is either "forward", "turn left 90 degrees", or "turn right 90 degrees". You want to issue the shortest sequence of instructions that will guide her to her destination. If you instruct Bessie to move off the grid (i.e., into the barn wall) or into a haybale, she will not move and will skip to the next command in your sequence.

Unfortunately, Bessie doesn't know if she starts out facing up (towards cell 1,2) or right (towards cell 2,1). You need to give the shortest sequence of directions that will guide her to the goal regardless of which case is true. Once she reaches the goal she will ignore further commands.

贝西误把自己困在了FJ谷仓的一侧。因为她的视力很差,她在脱困时需要你的帮助。

谷仓的平面图由一个方形细胞图(即所画图)呈现,有些细胞(即单位)是空的,其他的则是不可通过的柴草堆。贝西从左下角开始(细胞1,1)想一路搬到右上角。你可以引导她,告诉她一个指令序列,指令可以为“前进”“左转90度”“右转90度”。你需要得出能够使她到达目的地所用的最短指令序列。如果你指示贝西离开谷仓或至柴草堆,她不会移动,会直接跳到下一个命令序列。

不幸的是,贝西不知道她一开始所朝的方向(可能是上或右),而序列无需考虑这种情况,只需到达目标即可。(最后一句的正确性待考证)

输入输出格式

输入格式:

The first line of input contains .

Each of the  following lines contains a string of exactly  characters, representing the barn. The first character of the last line is cell 1,1. The last character of the first line is cell N, N.

Each character will either be an H to represent a haybale or an E to represent an empty square.

It is guaranteed that cells 1,1 and  will be empty, and furthermore it is guaranteed that there is a path of empty squares from cell 1,1 to cell .

输出格式:

On a single line of output, output the length of the shortest sequence of directions that will guide Bessie to the goal, irrespective whether she starts facing up or right.

输入输出样例

输入样例#1:
20
输出样例#1:
4

                                                  



        这道题我看了半天别人的题解,为了很久以后我还能看懂,所以写题解来记录一下。

        此题为usaco20171月月赛金组

        这道题是一道动归加上spfa,用dis[x1][y1][x2][y2][d1][d2]来表示两条路分别在x1,y1,面向d1,和在x2,y2,面向d2,从起点出发要到这个状态所需的最短指令。spfa中,分三种情况来讨论三种做法,最后的得到结果。

        具体的解释和意思在代码注释里。

#include  
#include  
#include  
#include  
#include  
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 () {}  
};  
//上面这段定义,是我无耻地抄了一个博客,因为我完全不知道该怎么把那六个元素定义起来....  
//也是看了那个人的博客后才指导方法的。。。我们就姑且算作是定义了一个包含六个元素的集合吧。。。  
queueq;//定义一个队列  
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<

啊啊啊这个题我回家后再好好整理!!!

你可能感兴趣的:(C++)