hdu 1026 Ignatius and the Princess I 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1026
题目大意:经典二维迷宫问题,魔王、公主与勇士的故事,这不是魔塔么???!!!
题目分析:又是要求输出路径的BFS,还打小怪兽,不过不怕,poj3984打印过路径,hdu1242也打过小怪兽,这道题做起来就颇得心应手了。假设读者已经完全熟悉BFS的基本方法,下面就说一下两个难点。
(1)怎么打小怪兽:可以用优先队列,加入一个点的时候把这个点的怪兽血量加到步数里,把步数多的排到后面;其实根本不需要用优先队列,普通队列一样可以解决这个问题,只需要在扩增时把还有活怪兽的点怪兽血量减一,步数加一,重新入队,直到怪死了才允许往其他点上走。
(2)怎么记录路径:定义一个pre数组,大小同map,记录每个点的前驱,遍历时当一个点(first)找到下一个可走点(next)时就把下一个点对应位置的pre设为first,再稍注意一下边界,就能完美地记录下一条路径了。记录下来还不够,因为它是倒着的,很容易想到用栈,那么递归也可以(其实一维数组就能解决这个问题)。
至此全部问题分析完毕,AC代码奉上。
code:
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
using namespace std;
struct node
{
int x,y,step,hp;
friend bool operator<(node n1,node n2)
{
return n2.step<n1.step;
}
}pre[111][111];
int map[111][111],dir[4][2]={1,0,-1,0,0,1,0,-1},m,n;
bool vis[111][111];
int temp;
void P(node p)
{
if(p.x==0&&p.y==0) return ;
P(pre[p.x][p.y]);
printf("%ds:(%d,%d)->(%d,%d)\n",temp++,pre[p.x][p.y].x,pre[p.x][p.y].y,p.x,p.y);
while(map[p.x][p.y]--) printf("%ds:FIGHT AT (%d,%d)\n",temp++,p.x,p.y);
}
bool judge(int x,int y)
{
return x<m&&y<n&&x>=0&&y>=0&&map[x][y]!=-1&&!vis[x][y];
}
node bfs()
{
priority_queue<node>q;
node first,next;
first.x=0,first.y=0,first.step=0,first.hp=0;
q.push(first);
while(!q.empty())
{
first=q.top();
q.pop();
if(first.x==m-1&&first.y==n-1)return first;
for(int i=0;i<4;i++)
{
next.x=first.x+dir[i][0];
next.y=first.y+dir[i][1];
if(judge(next.x,next.y))
{
next.step=first.step+1+map[next.x][next.y];
q.push(next);
vis[next.x][next.y]=true;
pre[next.x][next.y]=first;
}
}
}
return first;
}
int main()
{
int i,j;
char s[111];
while(scanf("%d%d",&m,&n)!=EOF)
{
memset(vis,false,sizeof(vis));
for(i=0;i<m;i++)
{
scanf("%s",s);
for(j=0;j<=n;j++)
{
switch(s[j])
{
case'.':map[i][j]=0;break;//平地
case'X':map[i][j]=-1;break;//墙
default:map[i][j]=s[j]-'0';//有怪存血
}
}
}
map[0][0]=0;
vis[0][0]=true;
node p=bfs();
if(!vis[m-1][n-1])printf("God please help our poor hero.\n");
else
{
printf("It takes %d seconds to reach the target position, let me show you the way.\n",p.step);
temp=1;
P(p);//输出用的递归
}
printf("FINISH\n");
}
return 0;
}
PS:两天A了这一道题,总共交了20遍,吐血了有木有……能改的地方都改了,连不可能出现的数据我都测试了,最后发现问题出在路径输出上,sad……
附wrong码,祭奠一下逝去的两天:
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct node
{
int x,y,step,hp;
}path[110000],pre[110][110];
int map[110][110],dir[4][2]={1,0,-1,0,0,1,0,-1},m,n;
bool vis[110][110];
bool judge(int x,int y)
{
return x<m&&y<n&&x>=0&&y>=0&&map[x][y]!=-1&&!vis[x][y];
}
node bfs()
{
queue<node>q;
node first,next;
first.x=0,first.y=0,first.step=0,first.hp=0;
q.push(first);
while(!q.empty())
{
first=q.front();
q.pop();
if(map[first.x][first.y]>0)
{
first.hp++;
first.step++;
map[first.x][first.y]--;
q.push(first);
continue;
}
for(int i=0;i<4;i++)
{
next.x=first.x+dir[i][0];
next.y=first.y+dir[i][1];
if(judge(next.x,next.y))
{
next.step=first.step+1;
next.hp=0;
q.push(next);
vis[next.x][next.y]=true;
pre[next.x][next.y]=first;
if(next.x==m-1&&next.y==n-1&&!map[next.x][next.y])return next;
}
}
}
return first;
}
int main()
{
int i,j;
char s[110];
while(scanf("%d%d",&m,&n)!=EOF)
{
memset(vis,false,sizeof(vis));
for(i=0;i<m;i++)
{
scanf("%s",s);
for(j=0;j<=n;j++)
{
switch(s[j])
{
case'.':map[i][j]=0;break;//???
case'X':map[i][j]=-1;break;//?
default:map[i][j]=s[j]-'0';//?й???
}
}
}
map[0][0]=0;
vis[0][0]=true;
node p=bfs();
int step=p.step;
if(!vis[m-1][n-1])printf("God please help our poor hero.\n");
else
{
printf("It takes %d seconds to reach the target position, let me show you the way.\n",p.step);
path[p.step]=p;
while(p.x!=0||p.y!=0||p.step)
{
if(pre[p.x][p.y].step==p.step-1)p=pre[p.x][p.y];
else p.step--;
path[p.step]=p;
}
for(i=0;i<step;i++)
{
printf("%ds:",i+1);
if(path[i].x==path[i+1].x&&path[i].y==path[i+1].y)printf("FIGHT AT (%d,%d)\n",path[i].x,path[i].y);
else
printf("(%d,%d)->(%d,%d)\n",path[i].x,path[i].y,path[i+1].x,path[i+1].y);
}
}
printf("FINISH\n");
}
return 0;
}/*后几组是自己想的,希望给wrong的同学一点帮助
5 6
.XX.1.
..X.2.
2...X.
...XX.
XXXXX.
5 6
.XX.1.
..X.2.
2...X.
...XX.
XXXXX1
5 6
.XX...
..XX1.
2...X.
...XX.
XXXXX.
5 6
1XX.1.
..X.2.
2...X.
...XX.
XXXXX1
5 6
1XX.1.
.1X12.
2.1.X4
...XX9
XXXXX1
5 6
1XX.1.
11X121
2111X4
...XX1
XXXXX1
5 6
......
......
......
......
......
5 6
.9...9
.9.9.9
.9.9.9
.9.9.9
X..9..
*/
上面的代码没用优先队列,而且是用数组存的结果。下面是个改成栈存的版本,感觉离真相近一点了没?
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
using namespace std;
struct node
{
int x,y,step,hp;
friend bool operator<(node n1,node n2)
{
return n2.step<n1.step;
}
}path[110000],pre[110][110];
int map[110][110],dir[4][2]={1,0,-1,0,0,1,0,-1},m,n;
bool vis[110][110];
bool judge(int x,int y)
{
return x<m&&y<n&&x>=0&&y>=0&&map[x][y]!=-1&&!vis[x][y];
}
node bfs()
{
priority_queue<node>q;
node first,next;
first.x=0,first.y=0,first.step=0,first.hp=0;
q.push(first);
while(!q.empty())
{
first=q.top();
q.pop();
if(first.x==m-1&&first.y==n-1)return first;
for(int i=0;i<4;i++)
{
next.x=first.x+dir[i][0];
next.y=first.y+dir[i][1];
if(judge(next.x,next.y))
{
next.step=first.step+1+map[next.x][next.y];
q.push(next);
vis[next.x][next.y]=true;
pre[next.x][next.y]=first;
}
}
}
return first;
}
int main()
{
int i,j;
char s[110];
while(scanf("%d%d",&m,&n)!=EOF)
{
memset(vis,false,sizeof(vis));
for(i=0;i<m;i++)
{
scanf("%s",s);
for(j=0;j<=n;j++)
{
switch(s[j])
{
case'.':map[i][j]=0;break;
case'X':map[i][j]=-1;break;
default:map[i][j]=s[j]-'0';
}
}
}
map[0][0]=0;
vis[0][0]=true;
node p=bfs();
int step=p.step,t=1;
if(!vis[m-1][n-1])printf("God please help our poor hero.\n");
else
{
printf("It takes %d seconds to reach the target position, let me show you the way.\n",p.step);
stack<node>q;
q.push(p);
while(p.x!=0||p.y!=0)
{
p=pre[p.x][p.y];
q.push(p);
}
p=q.top();
q.pop();
while(!q.empty())
{
printf("%ds:(%d,%d)->(%d,%d)\n",t++,p.x,p.y,q.top().x,q.top().y);
while(map[p.x][p.y]--)printf("%ds:FIGHT AT (%d,%d)\n",t++,p.x,p.y);
p=q.top();
q.pop();
}
}
printf("FINISH\n");
}
return 0;
}
PS:说好的DP,我必须回来了。明天开始,抛除一切杂念重返DP!