POJ 2449 Remmarguts' Date(第k短路+spfa+A*搜索)

题意:给出一个有向图,求出第k短路,节点可以重复经过。

思路:首先在反向边上进行一次spfa求出任一点至终点的最短距离,将这个距离作为A*搜索的h函数,估价函数f = g + h。注意这题有一个坑点,当起点和终点位于同一位置的时候,没经过任何边不算做一条路径。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXM = 100100;
const int MAXN = 1010; 
const int INF = 0x3f3f3f3f;
struct Edge {
    int to, next, w;
} edge[MAXM*2], edge2[MAXM*2];
int head[MAXN], head2[MAXN], tot, tot2;
int n, m;
struct Node {
	int f, g, cur;
	Node(int f=0, int g=0, int v=0) : f(f), g(g), cur(v) {} 
	bool operator < (const Node& A) const {
		return f==A.f ? g > A.g : f > A.f;
	}
}; 
void addedge(int u,int v,int w) {
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}
void addedge2(int u,int v,int w) {
    edge2[tot2].to = v;
    edge2[tot2].w = w;
    edge2[tot2].next = head2[u];
    head2[u] = tot2++;
}
bool vis[MAXN]; //在队列标志
int cnt[MAXN]; //每个点的入队列次数
int dist[MAXN];
bool spfa(int start, int n) {
	memset(vis, 0, sizeof(vis));
	for(int i = 1; i <= n; i++) dist[i] = INF;
	vis[start] = 1;
	dist[start] = 0;
	queue<int> q;
	while(!q.empty()) q.pop();
	q.push(start);
	memset(cnt, 0, sizeof(cnt));
	cnt[start] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = false;
		for(int i = head2[u]; i != -1; i = edge2[i].next) {
			Edge e = edge2[i];
			int v = e.to;
			if(dist[v] > dist[u]+e.w) {
				dist[v] = dist[u] + e.w;
				if(!vis[v]) {
					vis[v] = true;
					q.push(v);
					if(++cnt[v]>n) return false;
				}
			}
		}
	}
	return true;
}  
priority_queue<Node> q;
void init() {
    tot = tot2 = 0;
    memset(head, -1, sizeof(head));
    memset(head2, -1, sizeof(head2));
    while(!q.empty()) q.pop();
}
int st, ed, k;
int Astar() {
	if(dist[st] == INF) return -1;
	if(st == ed) k++;
	int cnt = 0;
	q.push(Node(dist[st], 0, st));
	while(!q.empty()) {
		Node t = q.top(); 
		q.pop();
		int u = t.cur;
		if(u == ed) cnt++;
		if(cnt == k) return t.f;
		for(int i = head[u]; i !=  -1; i = edge[i].next) {
			int v = edge[i].to, w = edge[i].w;
			q.push(Node(t.g+w+dist[v], t.g+w, v));
		}
	}
	return -1;
}
int main() {
    //freopen("input.txt", "r", stdin);
	while(cin >> n >> m) {
		init();
		for(int i = 1, u, v, d; i <= m; i++) {
			scanf("%d%d%d", &u, &v, &d);
			addedge(u, v, d);
			addedge2(v, u, d);
		}
		scanf("%d%d%d", &st, &ed, &k);
		spfa(ed, n);
		int ans = Astar();
		cout << ans << endl;
	}
    return 0;
}

















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值