POJ2449Remmarguts' Date(A*算法)

原题链接:http://poj.org/problem?id=2449
Remmarguts’ Date
Time Limit: 4000MS Memory Limit: 65536K
Total Submissions: 42272 Accepted: 11687
Description
“Good man never makes girls wait or breaks an appointment!” said the mandarin duck father. Softly touching his little ducks’ head, he told them a story.

“Prince Remmarguts lives in his kingdom UDF – United Delta of Freedom. One day their neighboring country sent them Princess Uyuw on a diplomatic mission.”

“Erenow, the princess sent Remmarguts a letter, informing him that she would come to the hall and hold commercial talks with UDF if and only if the prince go and meet her via the K-th shortest path. (in fact, Uyuw does not want to come at all)”

Being interested in the trade development and such a lovely girl, Prince Remmarguts really became enamored. He needs you - the prime minister’s help!

DETAILS: UDF’s capital consists of N stations. The hall is numbered S, while the station numbered T denotes prince’ current place. M muddy directed sideways connect some of the stations. Remmarguts’ path to welcome the princess might include the same station twice or more than twice, even it is the station with number S or T. Different paths with same length will be considered disparate.
Input
The first line contains two integer numbers N and M (1 <= N <= 1000, 0 <= M <= 100000). Stations are numbered from 1 to N. Each of the following M lines contains three integer numbers A, B and T (1 <= A, B <= N, 1 <= T <= 100). It shows that there is a directed sideway from A-th station to B-th station with time T.

The last line consists of three integer numbers S, T and K (1 <= S, T <= N, 1 <= K <= 1000).
Output
A single line consisting of a single integer number: the length (time required) to welcome Princess Uyuw using the K-th shortest path. If K-th shortest path does not exist, you should output “-1” (without quotes) instead.
Sample Input
2 2
1 2 5
2 1 4
1 2 2
Sample Output
14

题解:
题目原意s->k的第K短路多长,所以这是一道第k路的题目,主要用到的算法dijkstra或spfa和A*算法。

先说一下什么是第K路:
利用广度搜索BFS的话,第一次到的路径是最短的,第二次到的路径是次短路……第K次到的路径是第K短路。

如果直接BFS的话,那么会很慢,因为BFS和DFS是一种试探性搜索,往往不知道最好还是最坏,所以是一种最差的试探性搜索。然后就引入了A*启发式搜索,是一种优化搜索算法,也算是一种减枝的BFS或DFS。

那么A*算法是怎么样的呢:
f(n)=h(n)+g(n),f是一个估计函数,h是一个启发函数,表示从t终点()到其余一点的P的路径长度,g表示s(起点)到P所走的路径长度。(这究竟是什么意思,又有什么用呢)

借鉴一段话:原文链接:https://blog.csdn.net/sslz_fsy/article/details/82120232
如果给定一个“目标状态”,需要求出从初态到目标状态的最小代价。
1.那么优先队列BFS的这个“优先策略”显然是不完善的。一个状态的当前代价最小,只能说明从起始状态到该状态的代价很小,而在未来的搜索中,从该状态到目标状态可能会花费很大的代价。
2.另外一些状态虽然当前代价略大,但是未来到目标状态的代价可能会很小,于是从起始状态到目标状态的总代价反而更优。优先队列BFS会优先选择前者的分支.

在这里插入图片描述
求1->5的最短路
利用这个图说一下,从1开始有1->2和1->3两条边,明显是1->2小,那么假设我们走了这条路,然后一直走到5的话,1->2->3->4->5,。那如果我们牺牲一下,走1->3这条路,那么我们最后走的是1->3->4->5,明显比上一条路短。

如果我们知道未来的值,是不是就可知道哪一条路最近,这里我们用h来表示未来的值。但是这远远还不够,如果当前路径过大也会影响未来的值,所以我们不能单独靠一个h来确定当前路一定最短,那我们再加一个条件g,g表示当前的路径长度,假设f=h+g,未来值+当前值就可以得到一个比较稳定的估计值f,这个估计值就可以帮助我们选一条未来值h比较小,当前值g比较小的路径,这样就可以得到一条最短路了。

那么怎么得到未来值呢,那就从目标节点反向建边,找到所有节点的最小值,这样就可以求得未来值h。然后在通过加当前值g,就可以得到一个估计值f。

核心代码:
存储边2:

Struct node2{
	Int to;
	Int g,f;
	Bool operator < (const node2 &r) const{
		If(r.f==f)//比较估计值
			Return r.g<g;//比较当前值
		Return r.f<f;
	}
}

存储:1:

Struct node{
	Int to;
	Int value;
	Int next;
}

Spfa:
1.找出变小的节点,加入队列;
2.出队,更新节点,执行1操作
3.直到队列为空

void spfa(int s,int n,int head[],node1 edge[],int dist[]){
	for(int i=0;i<=n;i++){//初始化 
		dist[i]=INF;
		v[i]=0;
	}
	dist[s]=0;
	v[s]=1;
	queue<int> q;
	q.push(s);
	int temp,to,value;
	while(!q.empty()){
		temp=q.front();
		q.pop();
		v[temp]=0;
		for(int i=head[temp];i!=-1;i=edge[i].next){
			to=edge[i].to;
			value=edge[i].value;
			if(dist[to]>dist[temp]+value){
				dist[to]=dist[temp]+value;
				if(v[to]==0){
					v[to]=1;
					q.push(to);
				}
			}
		}
	}
}

A*算法:
1.判断s是否等于t,因为还没开始加边,所以算一次最短路,所以k++
2.如果dist[s]==INF,表示t无法到达s,结束算法
3.把起始节点s加入队列
4.出队,如果出队节点等于t,找到一次最短路cnt++,如果cnt等于k,表示找到了k次,返回e.g;
5.把出队的所有相邻节点的g,f更新一次,然后入队,重复4

int A_star(int s,int t,int n,int k,int head[],node1 edge[],int dist[]){
	node2 e,ne;
	int cnt=0;
	if(s==t){//目标等于源点,还没有开始加路径,不算第一次所以k++; 
		k++;
	}
	if(dist[s]==INF){//t到s不可达 
		return -1;
	}
	e.to=s;
	e.g=0;//还没开始加边,g=0; 
	e.f=e.g+dist[e.to];//f(n)=g(n)+h(n) 
	priority_queue<node2> q;
	q.push(e);
	while(!q.empty()){
		e=q.top();
		q.pop();
		if(e.to==t)
			cnt++;
		if(cnt==k)
			return e.g;
		for(int i=head[e.to];i!=-1;i=edge[i].next){
			ne.to=edge[i].to;
			ne.g=e.g+edge[i].value;
			ne.f=ne.g+dist[ne.to];//f(n)=g(n)+h(n) 
			q.push(ne);
		}
	}
	return -1; 
}

AC代码:

#include<iostream>
#include<string.h>
#include<queue>
#define INF 0x3f3f3f3f
#define MAXN 100010
using namespace std;
struct node1{
	int to;
	int value;
	int next;
};
struct node2{
	int to;
	int g,f;
	bool operator<(const node2 &r)const{
		if(r.f==f)//比较估计值 
			return r.g<g;//比较当前值 
		return r.f<f;
	}
};
struct node1 edge1[MAXN],edge2[MAXN];
int dist[1010],v[1001],head1[1001],head2[1001]; 
int cnt1,cnt2;
void init(){
	memset(head1,-1,sizeof(head1));
	memset(head2,-1,sizeof(head2));
	cnt1=cnt2=1;
}
void addedge(int from,int to,int value){
	edge1[cnt1].to=to;
	edge1[cnt1].value=value;
	edge1[cnt1].next=head1[from];
	head1[from]=cnt1++;
	edge2[cnt2].to=from;
	edge2[cnt2].value=value;
	edge2[cnt2].next=head2[to];
	head2[to]=cnt2++;
}
void spfa(int s,int n,int head[],node1 edge[],int dist[]){
	for(int i=0;i<=n;i++){//初始化 
		dist[i]=INF;
		v[i]=0;
	}
	dist[s]=0;
	v[s]=1;
	queue<int> q;
	q.push(s);
	int temp,to,value;
	while(!q.empty()){
		temp=q.front();
		q.pop();
		v[temp]=0;
		for(int i=head[temp];i!=-1;i=edge[i].next){
			to=edge[i].to;
			value=edge[i].value;
			if(dist[to]>dist[temp]+value){
				dist[to]=dist[temp]+value;
				if(v[to]==0){
					v[to]=1;
					q.push(to);
				}
			}
		}
	}
}
int A_star(int s,int t,int n,int k,int head[],node1 edge[],int dist[]){
	node2 e,ne;
	int cnt=0;
	if(s==t){//目标等于源点,还没有开始加路径,不算第一次所以k++; 
		k++;
	}
	if(dist[s]==INF){//t到s不可达 
		return -1;
	}
	e.to=s;
	e.g=0;//还没开始加边,g=0; 
	e.f=e.g+dist[e.to];//f(n)=g(n)+h(n) 
	priority_queue<node2> q;
	q.push(e);
	while(!q.empty()){
		e=q.top();
		q.pop();
		if(e.to==t)
			cnt++;
		if(cnt==k)
			return e.g;
		for(int i=head[e.to];i!=-1;i=edge[i].next){
			ne.to=edge[i].to;
			ne.g=e.g+edge[i].value;
			ne.f=ne.g+dist[ne.to];//f(n)=g(n)+h(n) 
			q.push(ne);
		}
	}
	return -1; 
}
int main(){
	int n,m,s,t,w;
	while(cin>>n>>m){
		init();
		for(int i=0;i<m;i++){
			cin>>s>>t>>w;
			addedge(s,t,w);
		}
		cin>>s>>t>>w;
		spfa(t,n,head2,edge2,dist);
		int ans=INF;
		ans=A_star(s,t,n,w,head1,edge1,dist);
		if(ans!=INF&&ans!=-1){
			cout<<ans<<endl;
		}else
			cout<<"-1"<<endl;
	}
	return 0;
}

学习博客:https://blog.csdn.net/Z_Mendez/article/details/47057461

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值