其他分享
首页 > 其他分享> > CCUT 2021寒假二期集训 训练赛1 补题

CCUT 2021寒假二期集训 训练赛1 补题

作者:互联网

C - Bob’s Problem

题目大意就是有黑边和白边,白边有数量限制,但是黑边没有,问连通图的最大权值和(注意已经联通之后也可以加边)。就是一个最大生成树的问题,但是需要贪心求解。

首先黑边只要有我们都可以连上,因为黑边只要少连了一条我们的权值和都会减少。然后将所有的白边排序,贪心的从权值大的边开始判断,如果这条边可以使当前图的联通块减少的话,就加入这条边,将题目给的白边数量减一,并标记这条边。

如果for一遍白边进行上述操作之后白边的数量还没有用完,就从权值大的白边遍历到权值小的白边,如果这条边没有被标记过,就将这条边加入,且将题目给白边数量减一,直到题目给的白边数量用完或者遍历完所有白边。

易错点
当时做这道题的时候很容易就看出来是一道最大生成树问题,但是细节地方一直没该明白,比如下面的样例中,一条边既是白边,又是黑边,那么实际上最后的答案是 29,也就是说黑边和白边是分开算的!

还有就是判断是否联通不能够按加入的边的数量是否等于n - 1来判断,因为这道题里即使联通了,依然可以加边,也就是说有n个点,但是联通时不一定有多少条边,所以最后需要判断一下是否只有一个联通块。

如下样例:
5
4 4 2
1 2 10 1
1 2 10 0
2 3 6 1
3 4 3 0

AC代码:

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define fi first
#define se second
#define endl '\n'
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
using namespace std;

int read()
{
	int x=0;
	char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x;
}

void write(int x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
	return;
}

const int M = 5e5 + 10;
const int N = 5e4 + 100;

int n, m, k;
int p[N], rk[N], bj[M];

struct node
{
	int a, b;
	int w;
	int color;
}edge[M];

void init()
{
	for(int i = 1; i <= n; i ++)
		p[i] = i, rk[i] = 0;
	memset(bj, 0, sizeof bj);
}

bool cmp(node a, node b)
{
	return a.w > b.w;
}

int find(int x)
{
	int son = x, tem;
	while(p[x] != x) x = p[x];
	while(son != x)
	{
		tem = p[son];
		p[son] = x;
		son = tem;
	}
	return x;
}

void unit(int r1, int r2)
{
	if(rk[r1] < rk[r2])
		p[r1] = r2;
	else if(rk[r2] > rk[r1])
		p[r2] = r1;
	else
		p[r2] = r1, rk[r1] ++;
}

int main()
{
	int t;
	t = read();
	while(t --)
	{
		vector<node> P;
		ll ans = 0, flag = 0, len = 0;
		n = read(), m = read(), k = read();
		init();
		for(int i = 1; i <= m; i ++)
		{
			edge[i].a = read(), edge[i].b = read(), edge[i].w = read(), edge[i].color = read();
			int r1 = find(edge[i].a), r2 = find(edge[i].b);
			if(edge[i].color) P.push_back(edge[i]);
			else ans += edge[i].w, unit(r1, r2), len ++;
		}
		sort(P.begin(), P.end(), cmp);
		for(int i = 0; i < P.size() && k; i ++)
		{
			node now = P[i];
			int r1 = find(now.a), r2 = find(now.b);
			if(r1 != r2)
			{
				k --;
				ans += now.w;
				unit(r1, r2);
				bj[i] = 1;
			}
		}
		for(int i = 0; i < P.size() && k; i ++)
			if(bj[i] == 0) k --, ans += P[i].w;
		for(int i = 1; i <= n; i ++) if(p[i] == i) flag ++;
		if(flag == 1) printf("%lld", ans);
		else write(-1);
		puts("");
	}
	return 0;
}


/*
5
4 4 2
1 2 10 1
1 2 9 0
2 3 6 1
3 4 3 0

10 + 9 + 6 + 3
*/

/*
5
4 4 1
1 2 10 1
1 2 9 0
2 3 6 1
3 4 3 0

9 + 6 + 3
*/

标签:r1,r2,int,白边,CCUT,edge,训练赛,补题,read
来源: https://blog.csdn.net/weixin_45925735/article/details/112486114