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