【最短路】【k短路】【Usaco2008 Mar】牛跑步

洛谷P2901

Description

BESSIE准备用从牛棚跑到池塘的方法来锻炼. 但是因为她懒,她只准备沿着下坡的路跑到池塘, 然后走回牛棚. BESSIE也不想跑得太远,所以她想走最短的路经. 农场上一共有M (1 <= M <= 10,000)条路, 每条路连接两个用1..N(1 <= N <= 1000)标号的地点. 更方便的是,如果X>Y,则地点X的高度大于地点Y的高度. 地点N是BESSIE的牛棚;地点1是池塘. 很快, BESSIE厌倦了一直走同一条路.所以她想走不同的路,更明确地讲,她想找出K (1 <= K <= 100)条不同的路经.为了避免过度劳累,她想使这K条路经为最短的K条路经. 请帮助BESSIE找出这K条最短路经的长度.你的程序需要读入农场的地图, 一些从X_i到Y_i 的路经和它们的长度(X_i, Y_i, D_i). 所有(X_i, Y_i, D_i)满足(1 <= Y_i < X_i; Y_i < X_i <= N, 1 <= D_i <= 1,000,000).

Input

* 第1行: 3个数: N, M, 和K 
* 第 2..M+1行: 第 i+1 行包含3个数 X_i, Y_i, 和 D_i, 表示一条下坡的路.

Output

* 第1..K行: 第i行包含第i最短路经的长度,或-1如果这样的路经不存在.如果多条路经有同样的长度,请注意将这些长度逐一列出.

Sample Input

5 8 7
5 4 1
5 3 1
5 2 1
5 1 1
4 3 4
3 1 1
3 2 1
2 1 1

Sample Output

1
2
2
3
6
7
-1
//路经分别为(5-1), (5-3-1), (5-2-1), (5-3-2-1), (5-4-3-1),(5-4-3-2-1).

分析

这个题是一个k短路的板子题,只需要在找到每条路径时记录一下就好

在建图的时候,我们可以反向再建一张图,然后用反向建出来的这张图求出每个节点到终点的最短路,也就是说把终点 t 看成起点求它与每个节点的最短路径。

然后从起点s到终点t的路径就变成了从s到当前节点 + 从当前节点到t,因为后者我们已经算出来了,所以只需要正向跑一个最短路,同时使用一个启发式函数,每找到一条路径就将它保存下来,最后输出就ok了

代码

#include<bits/stdc++.h>
using namespace std;
struct edge {
    int v, w, next;
} e[100010], e1[100010];
int head[1010], head1[1010], cnt;

int n, m, k;
int dis[1010];
bool vis[1010];

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
    return x * f;
}

void insert(int u, int v, int w) {
    e[++cnt].v = v, e[cnt].w = w, e[cnt].next = head[u], head[u] = cnt;
    e1[cnt].v = u, e1[cnt].w = w, e1[cnt].next = head1[u], head1[u] = cnt;
	//反向建图 
}

void spfa() {
    memset(dis, 1e9, sizeof dis);
    queue<int> q;
    dis[n] = 0;
    vis[n] = 1;
    q.push(n);
    int u;
    while (!q.empty()) {
	u = q.front();
	q.pop();
	vis[u] = false;
	for (int i = head1[u]; i; i = e1[i].next) {
	    int v = e1[i].v, w = e1[i].w;
	    if (dis[v] > dis[u] + w) {
		dis[v] = dis[u] + w;
		if (!vis[v]) {
		    vis[v] = true;
		    q.push(v);
		}
	    }
	}
    }
}

struct node {
    int d, id;
    node () {}
    node (int dd, int di) {
	d = dd;
	id = di;
    }
    bool operator < (const node & x) const {
	return x.d < d;
    }
};

int ans[110], kk;
priority_queue<node> q;
void A_star() {
    q.push(node(dis[n], n));
    while (!q.empty()) {
	int d = q.top().d, num = q.top().id;
	q.pop();
	if (num == 1) {
	    ans[++kk] = d;
	    if (kk == k) return;
	}
	for (int i = head[num]; i; i = e[i].next) {
	    q.push(node(d - dis[num] + e[i].w + dis[e[i].v], e[i].v));
	}
    }
}

int main() {
    n = read(), m = read(), k = read();
    for (int i = 1; i <= m; i++) {
	int u = read(), v = read(), w = read();
	insert(u, v, w);
    }
    spfa();
    A_star();
    for (int i = 1; i <= k; i++) {
	if (ans[i]) printf("%d\n", ans[i]);
	else puts("-1");
    }
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值