【解题报告】暑假THOI最短路作业刷题笔记

最短路小合集

别名:SPFA 复活赛

展开目录

Waring

因为

  1. 这套题难度很大,有我完全做不来的题
  2. 最短路板子好久没打了现在只会个 SPFA
  3. 我妈说的不许干别的大概率包括写博客

所以

  1. 大概率补不完
  2. 会写得很潦草

P3385 【模板】负环

SPFA 还能打一百年!

根据 \(SPFA\) 的原理,在一个有 \(N\) 个点的无负环图中,每个点最多入队 \((N-1)\) 次就能找到两点间的最短路。

但负环由于每次遍历时 \(dis\) 都会更小,所以会一直陷在环里出不来,这时对应点的入队次数会 \(\ge N\).

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 2 * 1e4 + 5;
int t, n, m, head[N], to[N], nxt[N], weal[N], tot;
int dis[N], cnt[N];
bool vis[N];
inline void add(int u, int v, int w) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
}
void spfa(int s) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = 0x7ffffff, vis[i] = 0, cnt[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(dis[v] > dis[u] + weal[i]) {
				dis[v] = dis[u] + weal[i];
				if(!vis[v]) {if(++cnt[v] >= n) {puts("YES"); return; } vis[v] = 1; q.push(v); }
			}
		}
	}
	puts("NO");
}
int main() {
	scanf("%d", &t);
	while(t--) {
		scanf("%d%d", &n, &m);
		int u, v, w;
		for(int i = 1; i <= m; ++i) {
			scanf("%d%d%d", &u, &v, &w);
			add(u, v, w);
			if(w >= 0) add(v, u, w);
		}
		spfa(1);
		for(int i = 1; i <= n; ++i) head[i] = 0;
		tot = 0;
	}
	return 0;
}

P1119 灾后重建

省流:SPFA死了

正解 Floyd,Dij 可以硬卡过去,SPFA 死了。

这是 \(80pts\) 的SPFA:

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 2 * 1e4 + 5;
int n, m, q, head[N], to[N], nxt[N], weal[N]/*, day[N]*/, tot;
int dis[N], cnt[N];
bool vis[N];
inline void add(int u, int v, int w/*, int d*/) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
//	day[tot] = d;
}
void spfa(int s, int d) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = 0x7fffffff, vis[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
//			cerr << u << " " << v << "\n";
			if(cnt[u] > d || cnt[v] > d) continue;
			if(dis[v] > dis[u] + weal[i]) {
				dis[v] = dis[u] + weal[i];
				if(!vis[v]) {vis[v] = 1; q.push(v); }
			}
		}
	}
}
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i) scanf("%d", cnt + i);
	int u, v, w, d;
	for(int i = 1; i <= m; ++i) {scanf("%d%d%d", &u, &v, &w), ++u, ++v/*, d = max(cnt[u], cnt[v])*/; add(u, v, w); add(v, u, w); }
	scanf("%d", &q);
	while(q--) {
		scanf("%d%d%d", &u, &v, &d);
		++u, ++v;
		if(cnt[u] > d || cnt[v] > d) {puts("-1"); continue; }
		spfa(u, d);
		printf("%d\n", dis[v]);
	}
	return 0;
}

不知道的还以为我在做红黑树呢:

image


P1656 炸铁路

THOI 这最短路题单里怎么这么多正解不是最短路的题啊甚至还混了道数论

SPFA 糊了半天最后翻了翻题解看到一个很有意思的解法:

在做最短路的时候排掉这条边,如果无解就是关键边。

然后排个序就可以了。

SPFA 看完尸斑都淡了。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 1e4 + 5;
int n, m, q, k, head[N], to[N], nxt[N], weal[N], tot;
int dis[N], cnt[N];
bool vis[N];
inline void add(int u, int v, int w) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
}
void spfa(int s, int f) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = 0x7fffffff, vis[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(v == f && u == s) continue;
			if(dis[v] > dis[u] + weal[i]) {
				dis[v] = dis[u] + weal[i];
				if(!vis[v]) {vis[v] = 1; q.push(v); }
			}
		}
	}
}
int u[N], v[N];
bool cmp(int a, int b) {
	if(u[a] != u[b]) return u[a] < u[b];
	return v[a] < v[b];
}
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; ++i) {scanf("%d%d", u + i, v + i); add(u[i], v[i], 1); add(v[i], u[i], 1); }
	for(int i = 1; i <= m; ++i) {
		spfa(u[i], v[i]);
		if(dis[v[i]] >= 0x3f3f3f3f) ++k, cnt[k] = i;
		if(u[i] > v[i]) swap(u[i], v[i]);
	}
	sort(cnt + 1, cnt + 1 + k, cmp);
	for(int i = 1; i <= k; ++i) printf("%d %d\n", u[cnt[i]], v[cnt[i]]);
	return 0;
}

P5905 【模板】全源最短路(Johnson)

提前声明:不是我自己改的

用 SPFA 写 Johnson 是需要点玄学的。

采用优化:

  1. LLL
  2. SLF
  3. 出队时判断队首和队尾大小,把大的放队首

感谢 @lxyt_415x 倾情相助。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 1e5 + 5, inf = 1e9;
ll n, m, head[N], to[N], nxt[N], weal[N], tot;
ll d[N], dis[N], cnt[N];
bool vis[N];
inline void add(ll u, ll v, ll w) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
}
bool mi(ll s) {
	queue<ll> q;
	for(int i = 1; i <= n; ++i) d[i] = inf, vis[i] = 0, cnt[i] = 0;
	q.push(s);
	d[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(d[v] > d[u] + weal[i]) {
				d[v] = d[u] + weal[i];
				if(!vis[v]) {vis[v] = 1; q.push(v); if(++cnt[v] > n) {return 1; } }
			}
		}
	}
	return 0;
}
void spfa(int s) {
	ll sum = 0, t = 0; 
	deque<ll> q;
	for(int i = 1; i <= n; ++i) dis[i] = inf, vis[i] = 0;
	q.push_back(s);
	dis[s] = 0, vis[s] = 1, sum += dis[s], ++t;
	while(!q.empty()) {
		int u = q.front();
		q.pop_front();
		if(!q.empty() && dis[q.front()] < dis[q.back()]) swap(q.front(), q.back());
		vis[u] = 0, sum -= dis[u], --t;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(dis[v] > dis[u] + weal[i]) {
				dis[v] = dis[u] + weal[i];
				if(!vis[v]) {
					vis[v] = 1; 
					if(!q.empty() && dis[v] <= dis[q.front()] && dis[v] <= sum / t) q.push_front(v);
					else q.push_back(v);
					sum += dis[v], ++t;
				}
			}
		}
	}
}
int main() {
	scanf("%lld%lld", &n, &m);
	int u, v, w;
	for(int i = 1; i <= m; ++i) {
		scanf("%d%d%d", &u, &v, &w);
		add(u, v, w);
	}
	for(int i = 1; i <= n; ++i) add(0, i, 0);
	if(mi(0)) {puts("-1"); return 0; } 
	for(int i = 1; i <= n; ++i) for(int j = head[i]; j; j = nxt[j]) weal[j] += d[i] - d[to[j]];
	for(int i = 1; i <= n; ++i) {
		spfa(i);
		ll ans = 0;
		for(ll j = 1; j <= n; ++j) {
			if(dis[j] != inf) ans += j * (dis[j] + (d[j] - d[i]));
			else ans += j * inf;
		}
		printf("%lld\n", ans);
	}
	return 0;
}

image
image
image

关于挑战:
image
image
image
所以:
image


P1144 最短路计数

遍历一条边时,已经记录的到终点的最短路长度若比到起点的最短路长度大,说明是一条新的最短路。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 4 * 1e6 + 5, mod = 1e5 + 3;
int t, n, m, head[N], to[N], nxt[N], tot;
ll dis[N], ans[N];
bool vis[N];
inline void add(int u, int v) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
}
void spfa(int s) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = 0x7ffffff, vis[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(dis[v] == dis[u] + 1) ans[v] += ans[u] , ans[v] %= mod;
			if(dis[v] > dis[u] + 1) {
				ans[v] = ans[u] % mod;
				dis[v] = dis[u] + 1;
				if(!vis[v]) vis[v] = 1, q.push(v);
			}
		}
	}
}
int main() {
	ans[1] = 1;
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; ++i) {
		int u, v;
		scanf("%d%d", &u, &v);
		add(u, v);
		add(v, u);
	}
	spfa(1);
	for(int i = 1; i <= n; ++i) printf("%lld\n", ans[i]);
	return 0;
}

P1629 邮递员送信

把图反着建一遍然后再跑,把两遍的最短路加起来就行。和 P1342 是双倍经验。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 1e5 + 5;
int t, n, m, head[N], to[N], nxt[N], weal[N], tot;
ll ans;
ll dis[N], cnt[N];
bool vis[N];
inline void add(int u, int v, int w) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
}
void spfa(int s) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = 0x7ffffff, vis[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(dis[v] > dis[u] + weal[i]) {
				dis[v] = dis[u] + weal[i];
				if(!vis[v]) vis[v] = 1, q.push(v);
			}
//			cerr << u << " " << v << " " << weal[i] << " " << dis[v] << " " << i << endl;
		}
	}
}
int u[N], v[N], w[N]; 
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; ++i) {scanf("%d%d%d", u + i, v + i, w + i); add(u[i], v[i], w[i]); }
	spfa(1);
	head[1] = 0;
	for(int i = 2; i <= n; ++i) {ans += dis[i], head[i] = 0; /*cerr << dis[i] << " "; */}
	tot = 0;
//	cerr << endl;
	for(int i = 1; i <= m; ++i) {add(v[i], u[i], w[i]); /*cerr << v[i] << " " << u[i] << " " << w[i] << endl;*/ }
//	cerr << endl;
	spfa(1);
	for(int i = 2; i <= n; ++i) {ans += dis[i]; /*cerr << dis[i] << " ";*/ }
//	cerr << endl;
	printf("%lld\n", ans);
	return 0;
}

image


P1673 [USACO05FEB] Part Acquisition S

感觉是板子,加个判无解就行。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 1e5 + 5;
int t, n, m, head[N], to[N], nxt[N], weal[N], tot;
ll dis[N], cnt[N];
bool vis[N];
inline void add(int u, int v, int w) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
}
void spfa(int s) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = 0x7fffffff, vis[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(dis[v] > dis[u] + weal[i]) {
				dis[v] = dis[u] + weal[i];
				if(!vis[v]) vis[v] = 1, q.push(v);
			}
		}
	}
}
int main() {
	scanf("%d%d", &n, &m);
	int u, v;
	for(int i = 1; i <= n; ++i) scanf("%d%d", &u, &v), add(u, v, 1);
	spfa(1);
	if(dis[m] == 0x7fffffff) puts("-1");
	else printf("%lld\n", dis[m] + 1);
	return 0;
}

P8674 [蓝桥杯 2018 国 B] 调手表

把按一下当成 \(i \rightarrow i+1\) 的一条边,按 \(k\) 按钮同理,建边的时候取个模,跑完之后去所有最短路的最大值。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 2 * 1e5 + 5;
int t, n, k, head[N], to[N], nxt[N], weal[N], tot;
ll dis[N], cnt[N], ans;
bool vis[N];
inline void add(int u, int v, int w) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
}
void spfa(int s) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = 0x7fffffff, vis[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(dis[v] > dis[u] + weal[i]) {
				dis[v] = dis[u] + weal[i];
				if(!vis[v]) vis[v] = 1, q.push(v);
			}
		}
	}
}
int main() {
	scanf("%d%d", &n, &k);
	for(int i = 0; i < n; ++i) add(i, (i + 1) % n, 1), add(i, (i + k) % n, 1);
	spfa(0);
	for(int i = 1; i < n; ++i) ans = max(dis[i], ans);
	printf("%lld\n", ans);
	return 0;
}

image


P7551 [COCI2020-2021#6] Alias

我可太喜欢 map 了。

除了 map 的部分都是纯板子,记得开long long。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 2 * 1e4 + 5;
const ll inf = 2e63;
int t, n, m, head[N], to[N], nxt[N], weal[N], tot, word;
ll dis[N], cnt[N];
bool vis[N];
map<string, int> mp;
inline void add(int u, int v, int w) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
}
void spfa(int s) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = inf, vis[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(dis[v] > dis[u] + weal[i]) {
				dis[v] = dis[u] + weal[i];
				if(!vis[v]) vis[v] = 1, q.push(v); 
			}
		}
	}
}
int main() {
	scanf("%d%d", &n, &m);
	string u, v; int w;
	for(int i = 1; i <= m; ++i) {
		cin >> u >> v >> w;
		if(mp.find(u) == mp.end()) mp[u] = ++word;
		if(mp.find(v) == mp.end()) mp[v] = ++word;
		add(mp[u], mp[v], w);
	}
	scanf("%d", &t);
	while(t--) {
		cin >> u >> v;
		if(mp.find(u) == mp.end()) mp[u] = ++word;
		if(mp.find(v) == mp.end()) mp[v] = ++word;
		spfa(mp[u]);
		if(dis[mp[v]] == inf) puts("Roger");
		else printf("%lld\n", dis[mp[v]]);
	}
	return 0;
}

P10110 [GESP202312 七级] 商品交易

SPFA秒了。

用一个数组单独存每个物品的价值,建边的时候长度就是 \((v_{y_i} - v_{x_i} + 1)\).

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 2 * 1e5 + 5;
const ll inf = 2e63;
int t, n, m, head[N], to[N], nxt[N], weal[N], tot, word;
ll dis[N], cnt[N];
bool vis[N];
inline void add(int u, int v, int w) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
}
void spfa(int s) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = inf, vis[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(dis[v] > dis[u] + weal[i]) {
				dis[v] = dis[u] + weal[i];
				if(!vis[v]) vis[v] = 1, q.push(v); 
			}
		}
	}
}
int main() {
	int a, b, u, v;
	scanf("%d%d%d%d", &n, &m, &a, &b), ++a, ++b;
	for(int i = 1; i <= n; ++i) scanf("%lld", cnt + i);
	for(int i = 1; i <= m; ++i) scanf("%d%d", &u, &v), ++u, ++v, add(u, v, cnt[v] - cnt[u] + 1);
	spfa(a);
	if(dis[b] == inf) puts("No solution");
	else printf("%lld\n", dis[b]);
	return 0;
}

P1396 营救

其实我很纠结要不要把这题放进来。

如你所见,我又被最短路作业里不是最短路的题给骗了。

不过还是有最短路解法的,好歹比其他的合适。

这里我用的是 \(Kruskal\) 解法。我对Kruskal的爱还是大于SPFA啊

我们知道 \(Kruskal\) 是一种贪心算法,搜索顺序是从小到大的。

于是我们知道,\(Kruskal\) 在将一个点连到另一个点时,经过的边的权值是递增的。

所以,当 \(s\)\(t\) 第一次联通时,正在遍历的那条边的边权是 \(s\)\(t\) 过程中,最小的边权最大值。

因此可以得到本题代码。

#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 2 * 1e5 + 5;
struct edge {
    int u, v, w;
} e[N];
int n, m, s, t, F[N];
bool cmp(edge a, edge b) {
    return a.w < b.w;
}
int find(int x) {
    if(F[x] == x) return x;
    return F[x] = find(F[x]);
}
void kruskal() {
    sort(e + 1, e + 1 + m, cmp);
    for(int i = 1; i <= m; ++i) {
        int fa_u = find(e[i].u), fa_v = find(e[i].v);
        if(fa_u != fa_v) F[fa_u] = fa_v;
        if(find(s) == find(t)) {printf("%d\n", e[i].w); return; }
    }
}
int main() {
    scanf("%d%d%d%d", &n, &m, &s, &t);
    for(int i = 1; i <= n; ++i) F[i] = i;
    for(int i = 1; i <= m; ++i) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
    kruskal();
    return 0;
}

P5651 基础最短路练习题

上手敲完 SPFA 才发现又被骗了。

不过因为异或也有结合律,实际上最短路也是可做的。

为了节省时间复杂度,可以只跑一边以 1 为起点的最短路。其他的用 dis[u] ^ dis[v] 来计算。

注意下数据范围。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 4 * 1e5 + 5, inf = 2e63;
int t, n, m, head[N], to[N], nxt[N], weal[N], tot;
ll dis[N], cnt[N];
bool vis[N];
inline void add(int u, int v, int w) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
}
void spfa(int s) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = inf, vis[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(dis[v] > (dis[u] ^ weal[i])) {
				dis[v] = (dis[u] ^ weal[i]);
				if(!vis[v]) vis[v] = 1, q.push(v);
			}
		}
	}
}
int main() {
	scanf("%d%d%d", &n, &m, &t);
	int u, v, w;
	for(int i = 1; i <= m; ++i) scanf("%d%d%d", &u, &v, &w), add(u, v, w), add(v, u, w);
	spfa(1);
	while(t--) {
		scanf("%d%d", &u, &v);
		printf("%lld\n", (dis[u] ^ dis[v]));
	}
	return 0;
}

P8802 [蓝桥杯 2022 国 B] 出差

还是挺有意思的一个题。

之前做最小生成树的时候遇到过类似的,思想是把终点的点权赋值到边权上然后跑最短路。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 1e5 + 5;
const ll inf = 2e63;
int t, n, m, p, c, head[N], to[N], nxt[N], weal[N], tot;
ll dis[N], cnt[N];
bool vis[N];
inline void add(int u, int v, int w) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
}
void spfa(int s) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = inf, vis[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
//			cerr << u << " " << v << endl;
			if(dis[v] > dis[u] + weal[i]) {
				dis[v] = dis[u] + weal[i];
				if(!vis[v]) vis[v] = 1, q.push(v);
			}
		}
	}
}
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i) scanf("%d", cnt + i);
	int u, v, w;
	for(int i = 1; i <= m; ++i) scanf("%d%d%d", &u, &v, &w), add(u, v, w + cnt[v]), add(v, u, w + cnt[u]); 
	spfa(1);
	printf("%lld\n", dis[n] - cnt[n]);
	return 0;
}

P1938 [USACO09NOV] Job Hunt S

很有意思的一个最长路。

我是存负边权然后跑的 SPFA,无穷情况判个负环就行了。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 1e5 + 5;
const ll inf = 2e63;
int t, n, m, p, c, head[N], to[N], nxt[N], weal[N], tot;
ll dis[N], cnt[N], ans = inf;
bool vis[N], fl;
inline void add(int u, int v, int w) {
	to[++tot] = v;
	nxt[tot] = head[u]; 
	head[u] = tot;
	weal[tot] = w;
}
void spfa(int s) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = inf, vis[i] = 0, cnt[i] = 0;
	q.push(s);
	dis[s] = 0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
//			cerr << u << " " << v << endl;
			if(dis[v] > dis[u] + weal[i] - t) {
				dis[v] = dis[u] + weal[i] - t;
				if(!vis[v]) {if(++cnt[v] >= n) {puts("-1"); fl = 1; return; } vis[v] = 1, q.push(v); }
			}
		}
	}
}
int main() {
	scanf("%d%d%d%d%d", &t, &m, &n, &c, &p);
	int u, v, w;
	for(int i = 1; i <= m; ++i) scanf("%d%d", &u, &v), add(u, v, 0);
	for(int i = 1; i <= c; ++i) scanf("%d%d%d", &u, &v, &w), add(u, v, w); 
	spfa(p);
	if(fl) return 0;
	for(int i = 1; i <= n; ++i) ans = min(ans, dis[i] - t);
	printf("%lld\n", -ans);
	return 0;
}

双倍经验:P2648 赚钱

把 SPFA 换成 Johnson 就过了。


P1576 最小花费

非常好 double,使我的大脑旋转。

边权存成 'z / 100',然后起点终点交换跑一遍最短路就成。

我调半天想不明白为啥全WA最后发现是变量名打错了也是十分逆天的。

展开代码
#include <bits/stdc++.h>
#define ll long long
#define MyWife Cristallo
using namespace std;
const int N = 2 * 1e5 + 5;
const double inf = 2e63;
ll k, n, m, c, head[N], to[N], nxt[N], tot;
double d[N], dis[N], cnt[N], weal[N], ans;
bool vis[N];
inline void add(ll u, ll v, double w) {
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	weal[tot] = w;
}
void spfa(int s) {
	queue<int> q;
	for(int i = 1; i <= n; ++i) dis[i] = inf, vis[i] = 0;
	q.push(s);
	dis[s] = 100.0, vis[s] = 1;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(dis[v] > dis[u] / (1 - weal[i])) {
				dis[v] = dis[u] / (1 - weal[i]);
				if(!vis[v]) vis[v] = 1, q.push(v);
			}
//			cerr << u << " " << v << " " << weal[i] << " " << dis[v] << " " << i << endl;
		}
	}
}
int main() {
	scanf("%d%d", &n, &m);
	int u, v, w;
	for(int i = 1; i <= m; ++i) scanf("%d%d%d", &u, &v, &w), add(u, v, w * 0.01), add(v, u, w * 0.01);
	scanf("%d%d", &u, &v);
	spfa(v);
	printf("%.8lf\n", dis[u]);
	return 0;
}
posted @ 2024-08-04 16:32  _Kiichi  阅读(95)  评论(4编辑  收藏  举报