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 n≤105 ,就算卡牌颜色没有重样的,也不可能多到 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)!