最短路徑樹定義、性質、模板及例題
阿新 • • 發佈:2022-05-28
最短路徑樹的定義
給定一個無向連通帶權圖\(G = (V, E)\),節點\(u\)的最短路徑樹可以定義為:
一個圖\(G\)的生成樹\(G_1 = (V, E_1)\),其中\(E_1\)為\(E\)的子集。在\(G_1\)中從點\(u\)到其他任何點的最短距離與在\(G\)中相同。
跑一遍Dijkstra演算法,使用陣列\(pre\)記錄每個點是有哪條邊更新的。
性質
-
根節點到其他所有點的最短距離與原圖中相同
證明:定義 -
在所有生成樹中,最短路徑樹滿足根節點到其他所有點的距離之和最短(Atcoder ABC252 E)
證明:由於根節點到其他任何點的距離都是原圖的最短距離,因此距離之和也一定是最短的。
模板
void dijkstra() { priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>> > heap; for(int i = 1; i <= n; i ++) d[i] = 1e18; heap.push({0, 1}); d[1] = 0; while(heap.size()) { auto t = heap.top(); heap.pop(); int ver = t.second; ll dist = t.first; if(st[ver]) continue; st[ver] = true; for(int i = h[ver]; ~i; i = ne[i]) { int j = e[i]; if(d[j] > dist + w[i]) { d[j] = dist + w[i]; heap.push({d[j], j}); pre[j] = i; } } } }
例題
- CF545E Paths and Trees(權值和最小的最短路徑樹)
思路:在更新的時候,如果d[j] = dist + w[i],則用權值最小的邊來更新
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; typedef long long ll; typedef pair<ll, int> pii; const int N = 300010, M = 2 * N; int n, m, u; int h[N], e[M], ne[M], idx; ll w[M], d[N]; int pre[N]; bool st[N], isin[M]; void add(int a, int b, ll c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++; } void dijkstra(int u) { priority_queue<pii, vector<pii>, greater<pii> > heap; heap.push({0, u}); for(int i = 1; i <= n; i ++) d[i] = 1e18; d[u] = 0; while(heap.size()) { auto t = heap.top(); heap.pop(); int ver = t.second; ll dist = t.first; if(st[ver]) continue; st[ver] = true; for(int i = h[ver]; ~i; i = ne[i]) { int j = e[i]; if(d[j] > dist + w[i]) { d[j] = dist + w[i]; heap.push({d[j], j}); pre[j] = i; } else if(d[j] == dist + w[i]) { if(w[i] < w[pre[j]]) { pre[j] = i; } } } } } int main() { scanf("%d%d", &n, &m); memset(h, -1, sizeof h); for(int i = 0; i < m; i ++) { int a, b; ll c; scanf("%d%d%lld", &a, &b, &c); add(a, b, c), add(b, a, c); } scanf("%d", &u); dijkstra(u); ll ans = 0; for(int i = 1; i <= n; i ++) { if(i != u) { ans += w[pre[i]]; } } printf("%lld\n", ans); for(int i = 1; i <= n; i ++) { if(i != u) { isin[pre[i] / 2] = true; } } for(int i = 0; i < m; i ++) { if(isin[i]) { printf("%d ", i + 1); } } printf("\n"); return 0; }
- Acwing349 黑暗城堡(最短路徑樹計數)
思路:對每個點可選邊的數量計數。在跑Dijkstra演算法的時候,如果d[j] = dist + w[i],那麼當前點可選邊數量+1;如果d[j] > dist + w[i],說明邊權需要更新,則當前點可選邊數量歸1。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
typedef pair<ll, int> pii;
const int N = 1010, M = N * N;
const ll mod = 2147483647;
int n, m;
int h[N], e[M], ne[M], idx;
ll w[M], d[N], cnt[N];
bool st[N];
void add(int a, int b, ll c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
void dijkstra()
{
priority_queue<pii, vector<pii>, greater<pii> > heap;
heap.push({0, 1});
for(int i = 1; i <= n; i ++) d[i] = 1e18;
d[1] = 0;
while(heap.size()) {
auto t = heap.top();
heap.pop();
int ver = t.second;
ll dist = t.first;
if(st[ver]) continue;
st[ver] = true;
for(int i = h[ver]; ~i; i = ne[i]) {
int j = e[i];
if(d[j] > dist + w[i]) {
d[j] = dist + w[i];
heap.push({d[j], j});
cnt[j] = 1;
}
else if(d[j] == dist + w[i]) {
cnt[j] ++;
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for(int i = 0; i < m; i ++) {
int a, b;
ll c;
scanf("%d%d%lld", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
dijkstra();
ll ans = 1;
for(int i = 2; i <= n; i ++) {
ans = ans * cnt[i] % mod;
}
printf("%lld\n", ans);
return 0;
}
- CF1005F Berland and the Shortest Paths(輸出k條最短路徑樹的方案)
思路:記錄每個點的可選邊。Dijkstra之後,通過dfs列舉方案。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 200010, M = 2 * N;
int n, m, k;
int h[N], e[M], ne[M], idx;
bool st[N];
int d[N];
vector<int> pre[N];
int ans[M];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void dijkstra()
{
priority_queue<pii, vector<pii>, greater<pii> > heap;
heap.push({0, 1});
for(int i = 1; i <= n; i ++) d[i] = 1e9;
d[1] = 0;
while(heap.size()) {
auto t = heap.top();
heap.pop();
int ver = t.second, dist = t.first;
if(st[ver]) continue;
st[ver] = true;
for(int i = h[ver]; ~i; i = ne[i]) {
int j = e[i];
if(d[j] > dist + 1) {
d[j] = dist + 1;
pre[j].clear();
pre[j].push_back(i);
heap.push({d[j], j});
}
else if(d[j] == dist + 1) {
pre[j].push_back(i);
}
}
}
}
void dfs(int u)
{
if(u > n) {
if(!k) exit(0);
for(int i = 0; i < m; i ++) {
printf("%d", ans[i]);
}
printf("\n");
k --;
return;
}
for(auto t : pre[u]) {
ans[t / 2] = 1;
dfs(u + 1);
ans[t / 2] = 0;
}
}
int main()
{
scanf("%d%d%d", &n, &m, &k);
memset(h, -1, sizeof h);
for(int i = 0; i < m; i ++) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
dijkstra();
ll num = 1;
for(int i = 2; i <= n; i ++) {
num = num * (ll)pre[i].size();
if(num > k) {
num = k;
break;
}
}
k = num;
printf("%d\n", k);
dfs(2);
return 0;
}