最短路径树小结
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最短路径树小结相关的知识,希望对你有一定的参考价值。
最短路径树小结
0.定义
原图的一个生成树,根到任意结点的路径等于原图的根到该节点的最短路。
c o s t S P T ≥ c o s t M S T cost_{SPT}\\ge cost_{MST} costSPT≥costMST ,即 S P T SPT SPT 的权值和不一定是最优的。
S P T ≠ M S T SPT\\ne MST SPT=MST。
1.如何求SPT
采用 d i j k s t r a dijkstra dijkstra,同时维护一个 p r e [ u ] pre[u] pre[u] 数组,表示结点 u u u对应的父亲这条边的编号。
for(register int i = head[u]; i != 0; i = edges[i].next) { // 找邻居
int next = edges[i].to;
long long w = edges[i].w;
if(dis[next] > dis[u] + w) { // 松弛操作
dis[next] = dis[u] + w;
Node nxt = {next, dis[next]};
pq.push(nxt);
pre[next] = i;
}
if(dis[next] == dis[u] + edges[i].w && edges[i].w < edges[pre[next]].w) {
pre[next] = i;
}
}
1.习题
CF545E Paths and Trees
输出 S P T SPT SPT的权值和 和 边。
板题
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') {
f = -1;
}
c = getchar();
}
while(c <= '9' && c >= '0') {
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
}
return x * f;
}
const long long INF = 0x3f3f3f3f3f3f3f3f;
const int Maxn = 3e5 + 5;
const int Maxm = 6e5 + 5;
int n, m, s, cnt = 1, head[Maxn], pre[Maxn];
long long dis[Maxn], ans[Maxn];
bool vis[Maxn];
struct Line { // to指向的点, w是边权, next是这一条边连接的下一条边是谁
int to;
int w;
long long next;
} edges[Maxm]; // 存所有的边
inline void Add(int a, int b, long long w) { // 添加边的函数
++cnt; // 边的编号
edges[cnt].to = b; // 第cnt条边指向点y
edges[cnt].w = w; // 第cnt条边的权值
edges[cnt].next = head[a]; // 第cnt条边的指向连接点x的第一条边
head[a] = cnt; // 将连接点x的第一条边更新为第cnt条边
return ;
}
struct Node {
int id;
long long dist;
bool operator < (const Node &cur) const {
return dist > cur.dist;
}
} ;
inline void Dijkstra(int start) {
priority_queue <Node> pq; // 优先队列维护当前权值最小
for(register int i = 1; i <= n; ++i) {
dis[i] = INF; // DP初始化
vis[i] = false;
}
dis[start] = 0; // DP的边界条件
Node Start = {s, 0};
pq.push(Start); // 确定源点到源点的最短路径 将源点放入优先队列进行广搜
while(!pq.empty()) {
Node now = pq.top();
pq.pop();
int u = now.id; // 中转点
if(vis[u] == true) {
continue; // 防止多次将一个点加入堆中
}
vis[u] = true;
for(register int i = head[u]; i != 0; i = edges[i].next) { // 找邻居
int next = edges[i].to;
long long w = edges[i].w;
if(dis[next] > dis[u] + w) { // 松弛操作
dis[next] = dis[u] + w;
Node nxt = {next, dis[next]};
pq.push(nxt);
pre[next] = i;
}
if(dis[next] == dis[u] + edges[i].w && edges[i].w < edges[pre[next]].w) {
pre[next] = i;
}
}
}
return ;
}
signed main() {
n = read(), m = read();
for(register int i = 1; i <= m; ++i) { // m条边
int x = read(), y = read();
long long w = read();
Add(x, y, w), Add(y, x, w); // 双向边
}
s = read();
Dijkstra(s);
long long sum = 0, tot = 0;
for(register int i = 1; i <= n; ++i) {
if(i == s) {
continue; // 源点到自己无边
}
int id = pre[i];
long long w = edges[id].w;
sum += w;
ans[++tot] = id; // 记录边的编号
}
sort(ans + 1, ans + tot + 1);
printf("%lld\\n", sum);
for(register int i = 1; i <= tot; ++i) { // 向下取整
printf("%lld ", ans[i]>>1);
}
puts("");
return 0;
}
CF545E Paths and Trees
显然删到 ≤ n − 1 \\le n-1 ≤n−1 条边是最好的。
先确定 S P T SPT SPT,然后 d f s dfs dfs扫一遍,每扫到一个边就 c n t + + cnt++ cnt++, c n t = k cnt=k cnt=k 直接返回。
#include<bits/stdc++.h>
using namespace std;
#define int long long // 坏习惯...
inline int read() {
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') {
f = -1;
}
c = getchar();
}
while(c <= '9' && c >= '0') {
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
}
return x * f;
}
const int INF = 0x3f3f3f3f3f3f3f3f;
const int Maxn = 3e5 + 5;
const int Maxm = 6e5 + 5;
int n, m, s, k, cnt=1, sum, head[Maxn], dis[Maxn], pre[Maxn];
bool vis[Maxn], ins[Maxm];
struct Line {
int to;
int w;
int next;
} edges[Maxm];
inline void Add(int a, int b, int w) {
++cnt;
edges[cnt].to = b;
edges[cnt].w = w;
edges[cnt].next = head[a];
head[a] = cnt;
return ;
}
struct Node {
int id;
int dist;
inline bool operator < (const Node &cur) const {
return dist > cur.dist;
}
} ;
void Dijkstra(int start) {
priority_queue <Node> pq;
for(register int i = 1; i <= n; ++i) {
dis[i] = INF;
vis[i] = false;
}
dis[start] = 0;
Node Start = {start, 0};
pq.push(Start);
while(!pq.empty()) {
Node now = pq.top();
pq.pop();
int u = now.id;
if(vis[u] == true) {
continue;
}
vis[u] = true;
for(register int i = head[u]; i != 0; i = edges[i].next) {
int next = edges[i].to, w = edges[i].w;
if(dis[next] - dis[u] > w) {
dis[next] = dis[u] + w;
Node nxt = {next, dis[next]};
pq.push(nxt);
pre[next] = i;
}
if(dis[next] == dis[u] + edges[i].w && edges[i].w < edges[pre[next]].w) {
pre[next] = i;
}
}
}
for(register int i = 1; i <= n; ++i) {
vis[i] = false;
}
return ;
}
void DFS(int x, int father) {
if(sum == k) {
puts("");
exit(0);
}
for(register int i = head[x]; i != 0; i = edges[i].next) {
int next = edges[i].to, w = edges[i].w;
if(next == father) {
continue;
}
if(pre[next] == i) {
++sum;
vis[next] = true;
printf("%d ", i>>1);
DFS(next, x);
}
}
return ;
}
signed main() {
n = read(), m = read(), k = read()bzoj4016 [FJOI2014]最短路径树问题