您的位置:首页 > 其它

Poj 3328的一种解法

2012-01-04 20:11 218 查看
题目描述:http://acm.pku.edu.cn/JudgeOnline/problem?id=3328

今天上午想把3328题做了,一看发现有点难。于是,便上网搜解题报告,最终还是没找到一个解题报告。决定自己把它搞定了。看了discussion,有人说要用优先队列和dij算法或者dfs再或者bfs,但是我没用过优先队列,上午的时间就拿来简单的学学优先队列,再复习一下dij算法。

下午又把题目读了一遍,发现和我上次做了的那个走迷宫的题极为相似,于是想用和上次一样的方法(BFS)来解决这个问题,不过这次的这个有点特别:有两个出发点要同时考虑,一个左脚,一个右脚;经过仔细思考,发现BFS是可以解决这个题,于是按着自己的想法写了个程序,在贡献了两次WA后换来了一次AC。

下面就来讲讲我的解题思路:

根据题意:我们需要注意一下几点(1)逃跑的这个人是从S标记的方格开始走的,进入每个S方格的可以是这个人的左脚,也可以是这个人的右脚(2)只要任何一支脚到达了T标记的方格,就完成一次逃跑,也就是说可以取得一个最小时间了(最优解)(3)用其中的一只脚进入一个方格后,另一支脚可以到达的方格是有限制(这个限制题目说的很清除了)。



解题的大体思路:用一个二维数组保存输入的数据,用一个三维数组保存从出发点到达每个坐标点(也就是每个小方块)所用的最小时间,为什么要用三维的呢?这正是这个题与众不同之处,对于每个坐标点我们要保留两个最小时间:一个是用左脚进入这个坐标点的最小时间,另一个当然就是用右脚进入这个坐标点所用最小时间。 然后就是BFS求到达T标记方块的最小时间。

下面就来看看源程序(程序各个变量的作用都做了详细的解释)

//http://acm.pku.edu.cn/JudgeOnline/problem?id=3328

//用BFS

#include <iostream>

#include <string>

#include <queue>

#define INF 1000000

using namespace std;

int w,h; //宽度和高度

int Grid[65][65];//记录每个坐标点的情况,注意一下:例如Grid[2][5],2对应的是竖直向下方向的坐标值

//5对应的是水平向右方向的坐标值

int d[65][65][2];//记录到的该坐标所需要的最少时间,最低维0下表对应左脚,1下表对应右脚,例如d[3][5][0]的值就是用左脚进入(3,5)点所用的最少时间 , d[3][5][1]的值就是用右脚进入(3,5)点所用的最少时间

int Min; //用于保存我需要的那个最少时间

void Init(int ww,int hh) //这个函数主要完成数据的读入和数组初始化工作

{

memset(Grid,0,sizeof(Grid)); //将Grid数组的每个元素值初始化为0

memset(d,12,sizeof(d));//给d数组的每个元素初始化一个很大的值,memset函数事实上是对字节的操作,d数组的中的每个元素的每个字节上都赋值为12,这样之后,每个元素的值其实是很大的,不信的话,你可以打印出来看看

Min=INF;

w=ww;

h=hh;

for(int i=1;i<=h;i++)

for(int j=1;j<=w;j++)

{

char ch;

cin>>ch;

if(ch>='1'&&ch<='9')

Grid[i][j]=ch-'0';

else if(ch=='S')

Grid[i][j]='S';

else if(ch=='T')

Grid[i][j]='T';

else if(ch=='X')

Grid[i][j]='X';

}

return ;

}

bool InRectangle(int i,int j) //判断坐标(i,j)是否在宽度和高度的允许范围内

{

return i>=1&&i<=h&&j>=1&&j<=w;

}

struct Pos //定义一个结构体来保存某一节点的情况

{

int x,y; //坐标值 ,需要注意一下在这个程序中x保存的是竖直向下为正方向的坐标值,

//y保存是水平向右为正方向的坐标值,这和题目上给出的图表的不一样,其实这都无所谓啦

int flag; //记录这个坐标点上的数字,或字母

};

void BFS()

{

queue<Pos>que;

Pos p;

for(int j=1;j<=w;j++)

{

if(Grid[h][j]=='S') //将所有起始点加入到队列中,注意任何一个起始点可以是左脚开始也可以是右脚开始

{

p.x=h;

p.y=j;

p.flag='S';

d[h][j][0]=d[h][j][1]=0; //用左脚和用右脚进入起始点,初始化值都为0

que.push(p);

}

}

while(!que.empty())

{

p=que.front();

que.pop();

if(p.flag=='T') //检查是否达到了T标记的坐标点,注意左脚、右脚进入的最小值都要检查

{

if(d[p.x][p.y][0]<Min)

Min=d[p.x][p.y][0];

if(d[p.x][p.y][1]<Min)

Min=d[p.x][p.y][1];

}

else //下面是两个结构几乎一样的if语句 一个左脚,一个右脚

{ //进入p点时用的是左脚

if(d[p.x][p.y][0]<INF) //说明p点用左脚到达过

for(int i=p.x-2;i<=p.x+2;i++)

for(int j=p.y+1;j<=p.y+3;j++)

{

if( InRectangle(i,j) //(i,j)必须在这个给出的长宽范围内

&& abs(i-p.x)+abs(j-p.y)<=3) //必须满足题目给出的限制条件

{

if(1<=Grid[i][j]&&Grid[i][j]<=9 && d[p.x][p.y][0]+Grid[i][j]<d[i][j][1] && d[p.x][p.y][0]+Grid[i][j]<Min)

{

Pos newp;

newp.x=i;

newp.y=j;

newp.flag=Grid[i][j];

que.push(newp); //新的节点进入队列

d[i][j][1]=d[p.x][p.y][0]+Grid[i][j];//更新用右脚进入(I,j)的最小时间

}

else if(Grid[i][j]=='T'&&d[p.x][p.y][0]<d[i][j][1])//&&d[p.x][p.y][0]<Min

{

Pos newp;

newp.x=i;

newp.y=j;

newp.flag=Grid[i][j];

que.push(newp);

d[i][j][1]=d[p.x][p.y][0];

}

}

}

//用的是右脚进入p点

if(d[p.x][p.y][1]<INF) //说明p点用右脚到达过

for(int i=p.x-2;i<=p.x+2;i++)

for(int j=p.y-3;j<=p.y-1;j++)

{

if( InRectangle(i,j)

&& abs(i-p.x)+abs(j-p.y)<=3)

{

if(1<=Grid[i][j]&&Grid[i][j]<=9 && d[p.x][p.y][1]+Grid[i][j]<d[i][j][0] && d[p.x][p.y][1]+Grid[i][j]<Min)

{

Pos newp;

newp.x=i;

newp.y=j;

newp.flag=Grid[i][j];

//newp.foot=0; //

que.push(newp);

d[i][j][0]=d[p.x][p.y][1]+Grid[i][j];

}

else if(Grid[i][j]=='T'&&d[p.x][p.y][1]<d[i][j][0])//&&d[p.x][p.y][1]<Min

{

Pos newp;

newp.x=i;

newp.y=j;

newp.flag=Grid[i][j];

//newp.foot=0; // 左脚

que.push(newp);

d[i][j][0]=d[p.x][p.y][1];

}

}

}

}

}

}

int main()

{

int ww,hh;

while(cin>>ww>>hh)

{

if(ww==0&&hh==0)break;

Init(ww,hh);

BFS();

if(Min!=INF)

cout<<Min<<endl;

else cout<<-1<<endl;

}

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: