农夫约翰正在一个新的销售区域对他的牛奶销售方案进行调查。
他想把牛奶送到 T 个城镇,编号为 1∼T。
这些城镇之间通过 R 条道路 (编号为 1 到 R) 和 P 条航线 (编号为 1 到 P) 连接。
每条道路 i 或者航线 i 连接城镇 Ai 到 Bi,花费为 Ci。
对于道路,0≤Ci≤10,000;然而航线的花费很神奇,花费 Ci 可能是负数(−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
。
数据范围
1≤T≤25000,
1≤R,P≤50000,
1≤Ai,Bi,S≤T
输入样例:
6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10
输出样例:
NO PATH
NO PATH
5
0
-95
-100
题意: 一个n点的有向图,其中有r条道路和p条航线,道路是双向的且边权非负,航向是单向的且权值可能为负数,求从起点s到各点的最短距离。
分析: 这道题很自然地可以想到用spfa,但是spfa会被网格图或菊花图卡成O(nm),用普通的spfa会T的很惨,同时由于出现了负边,不太能用dijkstra跑,其实硬要跑也是可以的,只不过不能用vis数组了,这会导致dijkstra的速度很慢,但其实它的速度仍然比被卡的spfa算法要快......
于是为了避免spfa被卡成O(nm)就出现了很多优化技巧,这里有一个比较简单的优化,那就是slf(small label first),也就是短边优先的规则,用一个双端队列保存结点,入队时根据dis[to]和dis[q.front()]的大小来决定入队头还是入队尾。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <deque>
//#define int long long
#define inf 0x3f3f3f3f
using namespace std;
int head[25005], cnt, vis[25005], dis[25005];
struct edge
{
int to, next, w;
}e[150005];
int n, r, p, s;
void add(int u, int v, int w)
{
e[++cnt].to = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
void spfa(int s)
{
memset(dis, 0x3f, sizeof dis);
deque<int> a;
a.push_front(s);
dis[s] = 0;
while(a.size())
{
int now = a.front();
a.pop_front();
vis[now] = false;
for(int i = head[now]; i; i = e[i].next)
{
int to = e[i].to;
if(dis[to] > dis[now]+e[i].w)
{
dis[to] = dis[now]+e[i].w;
if(!vis[to])
{
if(dis[to] > dis[a.front()])
a.push_back(to);
else
a.push_front(to);
vis[to] = true;
}
}
}
}
}
signed main()
{
cin >> n >> r >> p >> s;
for(int i = 1; i <= r; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(u, v, w), add(v, u, w);
}
for(int i = 1; i <= p; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
}
spfa(s);
for(int i = 1; i <= n; i++)
if(dis[i] == inf)
puts("NO PATH");
else
printf("%d\n", dis[i]);
return 0;
}