CodeForces - 125E MST Company

题意:

给定一个有 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;
}
发布了55 篇原创文章 · 获赞 0 · 访问量 1265

猜你喜欢

转载自blog.csdn.net/weixin_44059127/article/details/101942154