一本通:秘密的牛奶运输
次小生成树经典做法总结
定义:
严格次小生成树:生成树权值之和的第二小的数值的树
非严格次小生成树:生成树权值之和的数值第二小的的树
Sol1:(既可以求严格最小生成树,也可以求非严格)
先做一遍最小生成树,枚举每一条非树边,加上这条边,便形成了一个环,再去掉环上权值最大的一条树边,得到了一颗疑似次小生成树,再取个极值。
复杂度:O(mlogm+n^2+m)
实现细节:
去掉环上权值最大的一条树边:建立最小生成树,从每个点依次dfs处理出从该点到其他点的路径上权值的最大值
原理:
设T是G的一个最小生成树,对于非树边a,树边b,(+a,-b)后若仍是一个生成树,称为一个可行变换
由T进行一次可行变换得到的新生成树集合称为T的邻集
定理
次小生成树一定在最小生成树的邻集中
Sol2:每次去掉一个树边,再做一遍Kruskal算法,再求最小的生成树
Code:
#include<bits/stdc++.h>
#define LL long long
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 2010,M=40010,INF=0x3f3f3f3f;
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int c) {
ne[idx] = h[a], e[idx] = b, w[idx] = c, h[a] = idx++;
}
int n, m;
struct Edge {
int a, b, c;
bool operator < (const Edge& e) { return c < e.c; }
}ee[M];
int cnt;
int p[N];
int dist[N][N];
void dfs(int u, int p, int maxw,int id) {
for (int i = h[u]; ~i; i = ne[i]) {
int k = e[i];
if (p == k)continue;
dist[id][k] = max(maxw, w[i]);
dfs(k, u, dist[id][k],id);
}
}
int find(int x) {
if (p[x] != x)p[x] = find(p[x]);
return p[x];
}
bool st[M];
int main() {
freopen("in.in", "r", stdin);
memset(h, -1, sizeof h);
cin >> n >> m;
for (int i = 1; i <= n; i++)p[i] = i;
for (int i = 1; i <= m; i++) {
int a, b, c;
cin >> a >> b >> c;
ee[cnt++] = { a,b,c };
}
sort(ee, ee + cnt);
LL tot = 0;
for (int i = 0; i < cnt; i++) {
int a = ee[i].a, b = ee[i].b, w = ee[i].c;
int pa = find(a), pb = find(b);
if (pa != pb) {
st[i] = true;
p[pa] = pb;
add(a, b, w), add(b, a, w);
tot += w;
}
}
for(int i=1;i<=n;i++)
dfs(i,-1,0,i);
int res = INF;
for(int i=0;i<cnt;i++)
if (!st[i]) {
int a = ee[i].a, b = ee[i].b, w = ee[i].c;
if(w>dist[a][b])
res = min(res, w - dist[a][b]);
}
cout << res + tot;
}
浙公网安备 33010602011771号