poj1724 ROADS (spfa + A*)

题意:

给定一个有向图,每条边有一个花费,问在不超过总承受能力k的前提下,最短路为多少。

思路:

此题和求第k短路很像。

先求最短路,如果花费超出了在求次短路,超出了再求第3短路……

如何每次求出当前的最短路呢?

设当前节点为x,路程为x.dist,

假设从x可以扩展到两个节点i,j,设从i到终点的最短路为d[i],从j到终点的最短路为d[j],

若i.dist + d[i] < j.dist + d[j],则当前的最短路一定会走i而不是j。

d数组可以先对反向图以节点n为源点做一次spfa求出。

具体的思路见代码注释。


代码(1044K,16MS):

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>

#define INF 0x3f3f3f3f

using namespace std;

struct Node{
	int v, dist, tot, cost; // 当前节点,当前路程,当前路程加上当前节点到终点的最短路,当前花费
	Node() {}
	Node(int a, int b, int c, int d) : v(a), dist(b), tot(c), cost(d) {}
	bool operator < (const Node &n) const { // 优先队列用,每次取出i.dist+d[i]最小的
		if (tot != n.tot)
			return tot > n.tot;
		return dist < n.dist;
	}
};

struct Edge{
	int to, v, cost;
	Edge(int a, int b, int c) : to(a), v(b), cost(c) {}
};

int n, m, k;
vector<Edge> edges[105];
vector<Edge> redges[105]; // 反向图
int dist[105]; //dist[i]表示从节点i到终点的最短路
int vis[105];
void spfa() { // 求dist数组
	queue<int> q;
	memset(dist, 0x3f, sizeof(dist));
	memset(vis, 0, sizeof(vis));
	vis[n] = 1;
	dist[n] = 0;
	q.push(n);
	while (!q.empty()) {
		int current = q.front();
		q.pop();
		for (int i = 0; i < redges[current].size(); i++) {
			Edge e = redges[current][i];
			if (dist[current] + e.v < dist[e.to]) {
				dist[e.to] = dist[current] + e.v;
				if (!vis[e.to]) {
					vis[e.to] = 1;
					q.push(e.to);
				}
			}
		}
		vis[current] = 0;
	}
}

priority_queue<Node> q;
int spfaAstar() {
	while (!q.empty()) q.pop();
	if (dist[1] == 0x3f3f3f3f) return -1;
	Node start = Node(1, 0, dist[1], 0);
	q.push(start);
	while (!q.empty()) {
		Node cur = q.top();
		q.pop();
		if (cur.v == n) { // 如果当前节点为终点
			if (cur.cost <= k) return cur.dist; // 符合条件就return
		}
		for (int i = 0; i < edges[cur.v].size(); i++) {
			Edge e = edges[cur.v][i];
			if (cur.cost + e.cost > k) continue; // 很重要的一条剪枝,当前花费加这条路的花费大于k就减掉。
			q.push(Node(e.to, cur.dist + e.v, cur.dist + e.v + dist[e.to], cur.cost + e.cost)); // 将下一个点加入队列
		}
	}
	return -1;
}

int main() {
	while (~scanf("%d %d %d", &k, &n, &m)) {
		int a, b, c, d;
		for (int i = 0; i <= n; i++) {
			edges[i].clear();
			redges[i].clear();
		}
		for (int i = 0; i < m; i++) {
			scanf("%d %d %d %d", &a, &b, &c, &d);
			edges[a].push_back(Edge(b, c, d));
			redges[b].push_back(Edge(a, c, d)); // 反向图
		}
		spfa();
		int ans = spfaAstar();
		printf("%d\n", ans);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值