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;
}
今天上午想把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;
}
相关文章推荐
- POJ 1088滑雪的一种非记忆搜索的解法
- poj 2140的一种解法
- POJ-5353-Fence Repair(哈夫曼问题->贪心(一种解法两种做法))
- poj 1182 食物链的一种解法(详解),非向量法
- poj 1679次小生成树的两种解法: Prim和Kruskal
- POJ——3903(最长非降子序列的另一解法)
- 【第三弹】【POJ2392】【Space Elevator】【解法一】
- “Gnome Tetravex游戏, ZOJ1008”的一种解法 (上)
- D - Matrix Chain Multiplication 的一种解法
- 数字覆盖问题的一种解法
- POJ 1579 解法二用动态规划给递归剪枝,减少重复计算。此题一开始没想到用此法耗费了不少时间。
- bzoj1901树套树的一种解法
- POJ 1797 【一种叫做最大生成树的很有趣的贪心】【也可以用dij的变形思想~】
- PKU ACM 1007题“DNA Sorting”的一种解法
- “Rescue(营救),ZOJ1649”的一种解法和疑惑
- POJ 1207 HDOJ/HDU 1032 3n+1数链问题 绝对不水的解法
- 一道算法题的一种O(n)解法
- POJ1000题解法
- poj 1703 Find them, Catch them(种类并查集和一种巧妙的方法)
- FizzBuzz的一种解法