AcWing 342 && BZOJ2200 道路与航线(第四次重新来过)

题目链接 https://www.acwing.com/problem/content/344/

题目描述

农夫约翰正在一个新的销售区域对他的牛奶销售方案进行调查。

他想把牛奶送到T个城镇,编号为1~T。

这些城镇之间通过R条道路 (编号为1到R) 和P条航线 (编号为1到P) 连接。

每条道路 i 或者航线 i 连接城镇Ai到Bi,花费为Ci。

对于道路,0≤Ci≤10,000;然而航线的花费很神奇,花费CiCi可能是负数(−10,000≤Ci≤10,000)。

道路是双向的,可以从Ai到Bi,也可以从Bi到Ai,花费都是Ci。

然而航线与之不同,只可以从Ai到Bi。

事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台了一些政策:保证如果有一条航线可以从Ai到Bi,那么保证不可能通过一些道路和航线从Bi回到Ai。

由于约翰的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。

他想找到从发送中心城镇S把奶牛送到每个城镇的最便宜的方案。

输入格式

第一行包含四个整数T,R,P,S。

接下来R行,每行包含三个整数(表示一个道路)Ai,Bi,Ci。

接下来P行,每行包含三个整数(表示一条航线)Ai,Bi,Ci。

输出格式

第1..T行:第i行输出从S到达城镇i的最小花费,如果不存在,则输出“NO PATH”。

题解:

首先%%%lydrainbow 

明确说了, 有负边,因此Dijkstra他GG了,但是吧,有SPFA。由于lydrainbow出数据的时候对本题数据进行了特殊构造,因此SPFA他也GG了。题目中表明,负边只有在单向边才会有,对于一个DAG,求最短路可以根据拓扑序在线性时间求解,其实就是按照拓扑序走一遍更新距离,走完之后的距离就是最近的。

这个方法可以应用到本题,但是本题的图并不是一个DAG怎么办???首先既然双向边没有负边,可以先将双向边读入,构成一个无向图,然后求无向图的连通块,将每一个连通块缩成一个点,在读入有向边,就得到了一个DAG。对于这个DAG可以通过拓扑序求最短路,而每一个连通块内的点可以通过堆优化的Dijkstra求最短路,其实也很简单,就是在求拓扑序的过程中求最短路。

首先找DAG中所有入度为0的点所在的连通块以及S(起点)所在的连通块入队(要求拓扑序了),然后依次从队列中取出一个点(这个点表示一个连通块),然后找属于这个连通块点,求他们的最短路。如果求解过程中遇到一个点同样属于这个连通块,就放入到堆中继续求解,如果不在这个连通块内,说明是单向边,就将这个点所在的连通块的入度减去1,如果入度为0,则入队(求解拓扑序的过程)。

/**
 * Author : correct
 */
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
struct p{
	int x, dist;
	p(int x, int dist){this->x = x; this->dist = dist;}
	p(){}
	bool operator < (const p& a)const{return this->dist < a.dist;}
	bool operator > (const p& a)const{return this->dist > a.dist;}
};
#define ll long long
#define DEBUG cout << "----------------------------------------------\n"
#define mem(a, b) memset(a, b, sizeof a)
const int N = 25100, M = 50100 * 3, INF = 0x3f3f3f3f;
int head[N], nex[M], to[M], ed[M], cnt, belong[N], scc;
bool vis[N];
int in[N];
ll d[N];
queue<int > q;
priority_queue<p, vector<p>, greater<p> > pq;
void add(int a, int b, int c){
	++cnt;
	ed[cnt] = c;
	to[cnt] = b;
	nex[cnt] = head[a];
	head[a] = cnt;
}
void dfs(int x){
	belong[x] = scc;
	for (int i = head[x]; i; i = nex[i]){
		int y = to[i];
		if (!belong[y])dfs(y);
	}
}
int T, R, P, S;
int main(){
	freopen("in.in", "r", stdin);
	cnt = 0;
	scc = 0;
	mem(d, 0x3f);
#ifdef DEBUG
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
#endif
	cin >> T >> R >> P >> S;
	d[S] = 0;
	for (int i = 1; i <= R; i++){
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
		add(b, a, c);
	}
	for (int i = 1; i <= T; i++){
		if (!belong[i]){
			++scc;
			dfs(i);
		}
	}
	for (int i = 1; i <= P; i++){
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
		in[belong[b]]++;
	}
	q.push(belong[S]);

	for (int i = 1; i <= scc; i++){
		if (in[i] == 0)q.push(i);
	}
	while (q.size()){
		int ft = q.front();
		q.pop();
		for (int i = 1; i <= T; i++){
			if (belong[i] == ft){
				pq.push(p(i, d[i]));
			}
		}
		while (pq.size()){
			p t = pq.top();
			pq.pop();
			if (vis[t.x])continue;
			vis[t.x] = 1;
			for (int i = head[t.x]; i; i = nex[i]){
				int y = to[i];
				int c = ed[i];
				if (d[y] > d[t.x] + c){
					d[y] = d[t.x] + c;
					if (belong[y] == belong[t.x]){
						pq.push(p(y, d[y]));
					}
				}
				if (belong[y] != belong[t.x]){
					in[belong[y]]--;
					if (in[belong[y]] == 0){
						q.push(belong[y]);
					}
				}
			}
		}
	}
	for (int i = 1; i <= T; i++){
		if (d[i] >= INF)cout << "NO PATH\n";
		else cout << d[i] << "\n";
	}
	return 0;
}

我查错查了一下午,发现错在了一个很沙雕的地方。第一步找入度为0的点的所在的连通块的时候,应该遍历所有的点,我遍历了,但是循环变量 i 被我用一个其他的非循环变量代替了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值