先放题目:
在桌面上放着n张纸牌,每张纸牌有两面,每面都写着一个非负整数。你的邪王真眼可以看到所有牌朝上的一面和朝下的一面写的数字。现在你需要将一些牌翻过来,使得所有牌朝上的一面中,至少有一半的数字是一样的。请你求出最少需要翻几张牌,或者判断无解。
注意:在翻牌的时候,你不能把牌扔掉,不能偷偷把别的牌放进来,也不能用笔涂改牌上面的数字。
输入描述:
第一行包含一个整数nnn,表示牌的数量;
接下来n行,每行两个非负整数ai,bi,表示每张牌上写的两个数字,aii对应朝上的一面,bi对应朝下的一面。
输出描述:
如果有解,则输出一个整数,表示最少的翻牌次数,否则输出Impossible。
这道题刚开始做的时候就只想到了用map记录频率,因为自己太菜了所以写了几行后觉得实现不了就放弃了,后续看了大佬的方法豁然开朗,本质就是用两个map储存:一个用来存储上下两面数字出现的次数,另一个用来存储上面的数字的出现次数,因为是要将牌翻到上面,必然涉及到上面数字出现次数的问题,故要建立map单独存储。至于上下数字重复的问题,当出现一张牌正反两面相同的时候,我们选择把它看成是上面的,只记录一次即可!!
废话不多说,上代码!!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10;
map<int,int>mp;
map<int,int>ans;
int aim;
int n,a[N],b[N];
int max(int a,int b){return a>b?a:b;}
signed main()
{
cin>>n;
aim=(n+1)/2;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i];
int flag=1;//标志,flag=1时说明无解
for(int i=1;i<=n;i++)
{
if(a[i]==b[i]) mp[a[i]]++;//当上下面的数字一样时,只记一个
else
{
mp[a[i]]++;
mp[b[i]]++;
}
ans[a[i]]++;//为什么要记录上面的数字呢?
//(和求最小值有关喽)
if(mp[a[i]]>=aim||mp[b[i]]>=aim) flag=0;//边输入边看解的情况
//只要有一个符合就不是无解,之后再求最小值
}
if(flag){cout<<"Impossible";return 0;}
int res=0x3f3f3f3f;//res等于无限大
for(int i=1;i<=n;i++)
{
//把上下各个数字都遍历比较一遍,只要有可能完成翻一些牌后不小于aim的情况,就更新
if(mp[a[i]]>=aim)
res=min(res,max(aim-ans[a[i]],0));
if(mp[b[i]]>=aim)
res=min(res,max(aim-ans[b[i]],0));
}
cout<<res;
return 0;
}