题意:
给定一个有 n 个点的无向图,判断最小生成树是否唯一。
链接:
https://vjudge.net/problem/POJ-1679
解题思路:
考虑在 Kruskal 算法基础上进行判断。排序后,对于一段权值相同的边,先判断有多少条可以加入最小生成树,统计最终实际加入几条,若两者不相等,说明有环,且环包含至少两条相同权值的边,则最小生成树不唯一。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int maxm = 6e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
struct Edge{
int u, v, w;
bool operator < (const Edge &o) const{
return w < o.w;
}
} ei[maxn];
int pre[maxn];
int n, m;
int fin(int x){
return x == pre[x] ? x : pre[x] = fin(pre[x]);
}
int solve(){
sort(ei + 1, ei + 1 + m);
for(int i = 1; i <= n; ++i) pre[i] = i;
int ret = 0, cnt = 1, cnt1 = 0, cnt2 = 0;
for(int i = 1, j = 1; i <= m; ){
cnt1 = cnt2 = 0;
while(j <= m && ei[i].w == ei[j].w){
if(fin(ei[j].u) != fin(ei[j].v)) ++cnt2;
++j;
}
while(i < j){
int x = fin(ei[i].u), y = fin(ei[i].v);
if(x != y) pre[x] = y, ret += cnt < n ? ei[i].w : 0, ++cnt, ++cnt1;
++i;
}
if(cnt1 != cnt2) return -1;
}
return ret;
}
int main(){
// ios::sync_with_stdio(0); cin.tie(0);
int t; scanf("%d", &t);
while(t--){
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i){
scanf("%d%d%d", &ei[i].u, &ei[i].v, &ei[i].w);
}
int ret = solve();
if(ret == -1) printf("Not Unique!\n");
else printf("%d\n", ret);
}
return 0;
}
此外,还可以通过求非严格次小生成树来判断唯一性。对于一条不在最小生成树上的边(u,v,w),贪心替换掉生成树上 u -> v 路径上最大权值边,所有替换情况取小即是非严格最小生成树。实现用倍增即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int maxm = 6e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
vector<pii> G[maxn];
struct Edge{
int u, v, w;
bool operator < (const Edge &o) const{
return w < o.w;
}
} ei[maxn];
int pre[maxn], dep[maxn], fa[maxn][21], mx[maxn][21];
int n, m, p;
int fin(int x){
return x == pre[x] ? x : pre[x] = fin(pre[x]);
}
void dfs(int u, int f){
fa[u][0] = f, dep[u] = dep[f] + 1;
for(int i = 1; i <= 20; ++i){
fa[u][i] = fa[fa[u][i - 1]][i - 1];
mx[u][i] = max(mx[u][i - 1], mx[fa[u][i - 1]][i - 1]);
}
for(int i = 0; i < sz(G[u]); ++i){
int v = G[u][i].second, w = G[u][i].first;
if(v == f) continue;
mx[v][0] = w;
dfs(v, u);
}
}
int getMax(int u, int v){
if(dep[u] < dep[v]) swap(u, v);
int ret = 0;
for(int i = 20; i >= 0; --i){
if(dep[fa[u][i]] >= dep[v]) ret = max(ret, mx[u][i]), u = fa[u][i];
}
if(u == v) return ret;
for(int i = 20; i >= 0; --i){
if(fa[u][i] != fa[v][i]){
ret = max(ret, max(mx[u][i], mx[v][i]));
u = fa[u][i], v = fa[v][i];
}
}
return max(ret, max(mx[u][0], mx[v][0]));
}
int kruskal(){
sort(ei + 1, ei + 1 + m);
for(int i = 1; i <= n; ++i) pre[i] = i;
for(int i = 1; i <= n; ++i) G[i].clear();
int ret = 0, cnt = 1; p = 0;
for(int i = 1; i <= m; ++i){
int x = fin(ei[i].u), y = fin(ei[i].v);
if(cnt < n && x != y){
++cnt, ret += ei[i].w, pre[x] = y;
G[ei[i].u].pb({ei[i].w, ei[i].v}), G[ei[i].v].pb({ei[i].w, ei[i].u});
}
else ei[++p] = ei[i];
}
return ret;
}
int main(){
// ios::sync_with_stdio(0); cin.tie(0);
int t; scanf("%d", &t);
while(t--){
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i){
scanf("%d%d%d", &ei[i].u, &ei[i].v, &ei[i].w);
}
int ret = kruskal(), ret2 = inf;
dfs(1, 0);
for(int i = 1; i <= p; ++i){
int tmp = getMax(ei[i].u, ei[i].v);
ret2 = min(ret2, ret + ei[i].w - tmp);
}
if(ret == ret2) printf("Not Unique!\n");
else printf("%d\n", ret);
}
return 0;
}