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

C - Bob’s Problem

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

需要注意的是题目说了在一些顶点之间允许有多条边,并且允许自环,首先自环不影响联通性,而且因为题目保证边权值是非负数整数,所以所有的自环我们都需要加上。

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

这里为什么要优先将可以使图联通的白边加入,而不是将权值最大的白边加入呢?因为白边的可用数量有限,如果优先将权值最大的白边加入的话,有可能导致本来可以联通的图变得不能联通了,如下样例:

5
4 4 1
1 2 10 0
2 3 6 0
1 3 8 1
3 4 3 1

实际上答案应该是 10 + 6 + 3 

如果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;
		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);
		}
		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;
}

B-Who is the Champion

题意难读,结构体排序即可。

#include<bits/stdc++.h>
#define endl '\n'
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;

const int N = 5e5 + 10;

int a[150][150];

struct node
{
	int sco;
	int win;
	int bh;
}p[150];

bool cmp(node a, node b)
{
	if(a.sco == b.sco) return a.win > b.win;
	else return a.sco > b.sco;
}


int main()
{
	int n;
	cin >> n;
	for(int i = 1; i <= n; i ++)
	{
		p[i].bh = i, p[i].sco = 0, p[i].win = 0;
		for(int j = 1; j <= n; j ++)
			cin >> a[i][j];
	}
	if(n == 1) 
	{
		cout << 1 << endl;
		return 0;
	}
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= n; j ++)
		{
			if(i == j) continue;
			if(a[i][j] > a[j][i])
			{
				p[i].sco += 3;
				p[i].win += (a[i][j] - a[j][i]);
				p[j].win += (a[j][i] - a[i][j]);
			}
			else if(a[i][j] == a[j][i])
			{
				p[i].sco += 1;
				p[j].sco += 1;
			}
			else
			{
				p[j].sco += 3;
				p[i].win += (a[i][j] - a[j][i]);
				p[j].win += (a[j][i] - a[i][j]);
			}
		}
	sort(p + 1, p + n + 1, cmp);
	if(p[1].sco == p[2].sco && p[1].win == p[2].win) cout << "play-offs" << endl;
	else cout << p[1].bh << endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值