HDU 1026 Ignatius and the Princess I

这道题要做到两点:首先是会用bfs遍历图以得到最小时间;另外,难点其实是在于如何确定那条最短路径

所经过的所有点。bfs始终层次遍历,但这样的遍历会遇到一个问题:如果每个点所花费的代价是相同的,

那么如何走并不会影响总时间。可是现在的问题是:代价并不一样。那么就会有这样一个问题:如果还是

按普通的层次遍历,那么同一层上的节点可能会抢占下一层的节点。比如,同为第二层节点的a,b(其代价

分别为6和4),共有一个第三层的节点c,同时访问c都会分别花去a,b一个代价。可是现在如果a先访问,

访问后b将不能再访问。那么c的最小代价就是7了!其实呢,是5。由此可见,同一层次上的节点,如果

有相同的下一层的邻接点则存在“遍历竞争”。解决的办法是利用优先级队列自动将b排在a之前,即使是a

先入队的。

另外一个难题就是记录路径了!得承认这个问题困扰了我很长时间!!刚开始时想用队列记录下节点,然后

再dfs找出这条路。后来想了想,dfs遍历代价可能太大了吧。为哈一直要从前向后找呢?对不对呀。于是乎,

我就用一个坐标点(father_x,father_y)记录下每个节点的来源,也即其father的坐标,那么就可以从最终点出

发,依次向前找father啦。bfs的特点就是一个father(上一层节点)可能有很多child(下一层节点),但一个

child只会有一个father。(一个child被father遍历过,就不会让其他father遍历了)

 

AC代码:

#include<iostream>
#include<queue>
#include<stack>
using namespace std;

const int help[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
char map[100][100];
bool visited[100][100];
int row,col,sum_time;
bool flag;

struct node  //存放访问过的点的信息,自己的位置(x,y),父亲的位置(father_x,father_y)
{
	int x,y,father_x,father_y,time,flag;
	
	friend bool operator < (const node a,const node b)
	{
		return a.time > b.time;
	}
};

priority_queue<node> q1,q2;

void store_map()   //存贮图形
{
	for(int i=0;i<row;i++)
	{
		for(int j=0;j<col;j++)
		{
			cin>>map[i][j];
		}
	}
}

void judge_empty()   //由于q1,q2都是全局的,在每次使用之前都要清空
{
	while(!q1.empty())
		q1.pop();
	while(!q2.empty())
		q2.pop();
}

void bfs()
{
	judge_empty();   //清空后使用

	memset(visited,false,sizeof(visited));
	visited[0][0]=true;
	node start;   //初始化队列,头结点入队
	start.x=start.y=start.time=start.flag=start.father_x=start.father_y=0;
	q1.push(start);
	
	flag=false;   //标志是否达到(row-1,col-1)
	sum_time=0;   //记录总共用去的最小时间
	node tmp1,tmp2;
	while(!q1.empty() && !flag)
	{
		tmp1=q1.top();
		q1.pop();
		q2.push(tmp1);   //每次从q1中出对的点都要进入q2中,因为这每个点都要可能是路径上的点
		
		for(int k=0;k<4;k++)
		{
			int xx=tmp1.x+help[k][0];
			int yy=tmp1.y+help[k][1];
			
			if(xx>=0 && xx<row && yy>=0 && yy<col && !visited[xx][yy])  //在图形范围内,并没有访问过的点,可以访问
			{
				visited[xx][yy]=true;

				if(xx==row-1 && yy==col-1)
				{
					tmp2.x=xx;
					tmp2.y=yy;
					tmp2.father_x=tmp1.x;
					tmp2.father_y=tmp1.y;
					if(map[xx][yy]=='.')
					{
						tmp2.time=tmp1.time+1;
						tmp2.flag=0;
					}
					else
					{
						tmp2.time=tmp1.time+1+map[xx][yy]-'0';
						tmp2.flag=map[xx][yy]-'0';
					}
					q1.push(tmp2);
					flag=true;
					sum_time=tmp2.time;

					if(flag)    //这个最终点要在这里入q2队列
						q2.push(tmp2);
				}
				else if(map[xx][yy]=='.')
				{
					tmp2.x=xx;
					tmp2.y=yy;
					tmp2.father_x=tmp1.x;
					tmp2.father_y=tmp1.y;
					tmp2.time=tmp1.time+1;
					tmp2.flag=0;
					q1.push(tmp2);
				}
				else if(map[xx][yy]>='1' && map[xx][yy]<='9')
				{
					tmp2.x=xx;
					tmp2.y=yy;
					tmp2.father_x=tmp1.x;
					tmp2.father_y=tmp1.y;
					tmp2.time=tmp1.time+1+map[xx][yy]-'0';
					tmp2.flag=map[xx][yy]-'0';
					q1.push(tmp2);
				}
			}
		}
	}
}

void help_output()  //辅助输出,目的就是将q2中在路径上的点找出来,从最最终节点一直向前找其父亲节点
{
	stack<node> s;
	while(!q2.empty())
	{
		s.push(q2.top());
		q2.pop();
	}


	node tmp,tmp1;
	tmp1=s.top();
	q2.push(tmp1);
	s.pop();
	while(!s.empty())
	{
		tmp=s.top();
		s.pop();
		if(tmp1.father_x==tmp.x && tmp1.father_y==tmp.y)
		{
			tmp1.father_x=tmp.father_x;
			tmp1.father_y=tmp.father_y;
			q2.push(tmp);
		}			
	}
}



void output()   //按要求输出
{
	if(flag)
	{
        help_output();

		cout<<"It takes "<<sum_time<<" seconds to reach the target position, let me show you the way."<<endl;
		
		int Time=1;
		node temp1,temp2;
		temp1=q2.top();
		q2.pop();

		while(!q2.empty())
		{
			temp2=q2.top();
			q2.pop();
			
			cout<<Time++<<"s:("<<temp1.x<<","<<temp1.y<<")"<<"->("<<temp2.x<<","<<temp2.y<<")"<<endl;
			if(temp2.flag)
			{
				for(int i=0;i<temp2.flag;i++)
					cout<<Time++<<"s:FIGHT AT "<<"("<<temp2.x<<","<<temp2.y<<")"<<endl;
			}
			
			if(temp2.x==row-1 && temp2.y==col-1)
				break;
			else
			{
				temp1.x=temp2.x;
				temp1.y=temp2.y;
			}
		}
	}
	else
		cout<<"God please help our poor hero."<<endl;

	cout<<"FINISH"<<endl;
}

int main()
{
	while(cin>>row>>col)
	{
		store_map();

		bfs();

        output();
	}

	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值