CF204B题解

CF204B

题目大意

n n n 张卡牌,每张卡牌有正面和反面,每一面都会有它的颜色。

现在,每一张卡牌都正面朝上,我们可以通过翻卡牌,使得卡牌朝上的颜色发生变化。那么至少需要翻多少次才能使得所有卡牌中,有至少一半的卡牌朝上的颜色相同。

需要注意的点

  • 1.有的卡牌可能正反面颜色相同
  • 2.如果卡牌总数量 n n n 是奇数,那么一半的定义为 n + 1 2 \frac{n + 1} 2 2n+1

题解

Step.1 统计

如果要让至少一半的卡牌朝上颜色相同,那得先保证某种颜色的数量(排除正反面颜色相同的情况)至少有一半吧。

举个栗子
3
1 2
3 4
5 6

如果给你这样的数据,那就妥妥的输出 − 1 -1 1

其实我们可以用两个桶,记录一下每个数一共出现了多少次和初始时,每个数位于卡牌背面的数量。

代码实现
int a[maxn] , b[maxn];// a数组是每个数一共出现的次数,b数组是每个数位于卡牌背面的数量
cin >> x >> y;// x:正面颜色,y:反面颜色
a[x] ++;// 总的颜色数量统计 
if(x != y) {// 这里要特判正反面颜色是否相同,如果相同的话,那么这张牌翻在正面还是反面就都一样了,直接假设它反面没有颜色就行 
	a[y] ++;
	b[x] ++;
	// 如果不相同,再做统计 
}

Step.2 做翻牌处理

我们通过刚才的统计,发现了一些能通过翻牌符合题目要求的颜色,现在需要翻牌了。

我们知道了总颜色数量和背面颜色数量,拿总的减去背面的,就是正面的了!

如果正面的数量 ≥ n + 1 2 \geq{\frac{n + 1}2} 2n+1 ,那就不用翻了,直接答案是 0 就得了;

如果小于,那就翻掉一些反面的牌让期望的颜色回到正面。

其实翻掉 n + 1 2 − ( a [ i ] − b [ i ] ) {\frac{n + 1}2}-(a[i] - b[i]) 2n+1(a[i]b[i]) 张牌就够了,这样正好一半的牌。

然后拿上面那个式子取最小值就 ok 。

代码实现
if(a[i] >= (n + 1) / 2) {
	if(a[i] - b[i] < (n + 1) / 2) {
		minn = min(minn , (n + 1) / 2 - (a[i] - b[i]));
	}
	else minn = 0;
}

Step.3 离散化

我们风风火火干了这么些事情,一看数据范围: What ??颜色值 ≤ 1 0 9 \le10^9 109 ???难不成还得开一堆 map 统计数量?!

不至于吧!

n ≤ 1 0 5 n\le10^5 n105 ,就算卡牌颜色没有重样的,也不可能多到 1 0 9 10^9 109 ,顶多 2 × 1 0 5 2\times10^5 2×105 啊!

这样我们没必要大费周章开 map 了,给不同颜色一个编号,统计一下有多少种颜色出现过,拿编号去作为颜色代号,参与后面的处理就行了!

代码实现

这里只截了输入部分,但离散化需要贯穿全篇代码哦~

for(int i = 1 ; i <= n ; ++ i) {
	int x , y;
	cin >> x >> y;
	if(mp[x] == 0) mp[x] = ++ cnt;// 如果这种颜色没有出现过,把它注册一下,下同。
	if(mp[y] == 0) mp[y] = ++ cnt;
	a[mp[x]] ++;// 拿颜色代号去做处理。
	if(x != y) {
		a[mp[y]] ++;
		b[mp[y]] ++;
	}
}

完整代码

#include <bits/stdc++.h>
#define long long int// 这里把long long 注册成 int,main函数要用signed
using namespace std;
const int maxn = 2e5 + 5;
int n;
map <int , int> mp;
int cnt;
int a[maxn] , b[maxn];
int minn = 1e9 + 7;
signed main() {
	//freopen(".in" , "r" , stdin);
	//freopen(".out" , "w" , stdout);
	cin >> n;
	for(int i = 1 ; i <= n ; ++ i) {
		int x , y;
		cin >> x >> y;
		if(mp[x] == 0) mp[x] = ++ cnt;
		if(mp[y] == 0) mp[y] = ++ cnt;
		a[mp[x]] ++;
		if(x != y) {
			a[mp[y]] ++;
			b[mp[y]] ++;
		}
	}
	for(int i = 1 ; i <= cnt ; ++ i) {
		if(a[i] >= (n + 1) / 2) {
			if(a[i] - b[i] < (n + 1) / 2) {
				minn = min(minn , (n + 1) / 2 - (a[i] - b[i]));
			}
			else minn = 0;
		}
	}
	if(minn == 1e9 + 7) cout << "-1" << endl;
	else cout << minn << endl;
	return 0;
}



这篇题解耗费了作者一个周的时间,觉得不错就点个赞呗~

关注作者,让我们一起学习信息学竞赛(OI)!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值