题意:
给定一个有 n 个点、m 条边的无向带权图,求 1 号点度数为 k 的最小生成树。(n, k <= 5e3, m <= 1e5)
链接:
https://vjudge.net/problem/CodeForces-125E#author=0
解题思路:
度限制最小生成树。先求出除 1 号结点外的最小生成森林,记有 m 个最小生成森林联通块,若 m > k,则无解。否则,贪心地连 m 条边,得到一个初始解。接下来循环 k - m 次,每次让 1 号结点度数增加 1,遍历所有连接 1 号结点的边,若要加入这条边,则必然形成一个环,故需删去此环上的最大边,每次贪心选取增加量最小的边加入最小生成树。此过程可以用 dp 优化找环上最大边,即在最小生成树上 dp 预处理,每次加入新边时再更新。
参考代码:
#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 = 5e3 + 5;
const int maxm = 1e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
struct Edge{
int u, v, w, p;
bool operator < (const Edge &o) const{
return w < o.w;
}
} e1[maxm], e2[maxm], ei[maxm];
int G[maxn][maxn], dp[maxn];
int pre[maxn], vis[maxm];
int n, m, k, m1, m2;
int fin(int x){
return x == pre[x] ? x : pre[x] = fin(pre[x]);
}
void kruskal(){
sort(e2 + 1, e2 + 1 + m2);
for(int i = 1; i <= n; ++i) pre[i] = i;
for(int i = 1; i <= m2; ++i){
int u = e2[i].u, v = e2[i].v, p = e2[i].p;
int x = fin(u), y = fin(v);
if(x == y) continue;
G[u][v] = G[v][u] = p;
pre[x] = y, vis[p] = 1;
}
}
void dfs(int u, int f){
for(int i = 1; i <= n; ++i){
if(!G[u][i] || i == f) continue;
dp[i] = ei[G[u][i]].w > ei[dp[u]].w ? G[u][i] : dp[u];
dfs(i, u);
}
}
int main(){
// ios::sync_with_stdio(0); cin.tie(0);
scanf("%d%d%d", &n, &m, &k);
m1 = m2 = 0;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j) G[i][j] = 0;
for(int i = 1; i <= n; ++i) vis[i] = 0, dp[i] = -1;
for(int i = 1; i <= m; ++i){
int u, v, w; scanf("%d%d%d", &u, &v, &w);
if(u > v) swap(u, v);
if(u == 1) e1[++m1] = { u, v, w, i };
else e2[++m2] = { u, v, w, i };
ei[i] = { u, v, w, i };
}
kruskal();
int cnt = 0;
sort(e1 + 1, e1 + 1 + m1);
for(int i = 1; i <= m1; ++i){
int u = e1[i].u, v = e1[i].v, p = e1[i].p;
int x = fin(u), y = fin(v);
if(x == y) continue;
G[u][v] = G[v][u] = p;
dp[v] = -1, dfs(v, 1);
pre[x] = y, vis[p] = 1, ++cnt;
}
int tot = 0;
for(int i = 1; i <= n; ++i) tot += pre[i] == i;
if(tot > 1 || m1 < k || cnt > k) return printf("-1\n"), 0;
for(int d = cnt + 1; d <= k; ++d){
int mn = inf, p;
for(int i = 1; i <= m1; ++i){
if(vis[e1[i].p]) continue;
if(mn > e1[i].w - ei[dp[e1[i].v]].w){
mn = e1[i].w - ei[dp[e1[i].v]].w;
p = i;
}
}
vis[e1[p].p] = 1, vis[dp[e1[p].v]] = 0;
int u = ei[dp[e1[p].v]].u, v = ei[dp[e1[p].v]].v;
G[u][v] = G[v][u] = 0;
G[1][e1[p].v] = G[e1[p].v][1] = e1[p].p;
dp[e1[p].v] = -1, dfs(e1[p].v, 1);
}
printf("%d\n", n - 1);
for(int i = 1; i <= m; ++i) if(vis[i]) printf("%d ", i);
printf("\n");
return 0;
}