山东大学可达鸭杯程序设计比赛J题题解

先放题目:

在桌面上放着n张纸牌,每张纸牌有两面,每面都写着一个非负整数。你的邪王真眼可以看到所有牌朝上的一面和朝下的一面写的数字。现在你需要将一些牌翻过来,使得所有牌朝上的一面中,至少有一半的数字是一样的。请你求出最少需要翻几张牌,或者判断无解。

注意:在翻牌的时候,你不能把牌扔掉,不能偷偷把别的牌放进来,也不能用笔涂改牌上面的数字。

输入描述:

第一行包含一个整数nnn,表示牌的数量;

接下来n行,每行两个非负整数ai,bi,表示每张牌上写的两个数字,aii​对应朝上的一面,bi​对应朝下的一面。

输出描述:

如果有解,则输出一个整数,表示最少的翻牌次数,否则输出Impossible。

这道题刚开始做的时候就只想到了用map记录频率,因为自己太菜了所以写了几行后觉得实现不了就放弃了,后续看了大佬的方法豁然开朗,本质就是用两个map储存:一个用来存储上下两面数字出现的次数,另一个用来存储上面的数字的出现次数,因为是要将牌翻到上面,必然涉及到上面数字出现次数的问题,故要建立map单独存储。至于上下数字重复的问题,当出现一张牌正反两面相同的时候,我们选择把它看成是上面的,只记录一次即可!!

废话不多说,上代码!!!!

#include
#define int long long
using namespace std;
const int N=3e5+10;
mapmp;
mapans;
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<

你可能感兴趣的:(c++,算法,数据结构)