题解:P11323 【MX-S7-T1】「SMOI-R2」Happy Card
首先注意到,“炸”与“三带一”都是三张同样的牌带上另一张,那么二者可以合并为同一种出牌方式。
将所有的牌拆开来,优先拆成三张相同一组,然后是一对牌一组,最后是一张单牌。三张一组的牌需要打两次,一对牌和单牌只需要打一次。
记初始统计出来单牌、对子、三张相同的牌个数为 $a$ , $b$ , $c$ 。如果初始统计 $c\le a$ ,将对子、三张拆开来只会增加负担,直接输出 $a+b$ 。
对于 $a < c$ ,可以假设:
- 拆了 $x$ 对牌分给了单牌;
 - 拆了 $y$ 组三张相同的牌,一张分给单牌,两张分给对子;
 - 拆了 $z$ 组三张相同的牌,全分给了单牌。
 
由于可以将分成三个单牌的三张牌视为先分成对子和一张单牌,再将对子分为两张单牌,所以直接不管 $z$ 。
拆过牌后单牌数量为 $a+2x+y$ ,对子数量为 $b-x+y$ , 三张相同牌数量为 $c-y$ 。
在拆完牌后,如果三张相同牌数量大于单牌,即 $c-y>a+2x+y$ ,那么拆开对子或三张必然不劣:
- 拆对子。若 $c-y>a+2x+y+1$ ,必然会减少一次出牌;若 $c-y=a+2x+y+1$ ,出牌次数必然与不拆对子的次数相同。
 - 拆三张。若 $c-y>a+2x+y+1$ ,拆去的三张的两次出牌便不用出了,多出的一张单牌可以与另外三张相同的牌一起出,减少两次出牌又增加一次出牌,再加上多出的对子,总计少出两次牌;若 $c-y=a+2x+y+1$ 则多出的单牌没法配对了,出牌次数等于拆牌前。
 
那么就可以一直拆牌,直至拆到 $c-y\le a+2x+y+1$ ,整理得 $c-a \le 2(x+y)$ 则 $x+y \geq \lceil \frac{c-a}{2} \rceil$ 。此时单牌数量大于三张相同牌,所以出牌次数就为单牌数加上对子数 $a+b+x+2y$ 。
让出牌次数最小,就要让 $x$ 尽量大,即先拆对子,而一定要有 $x\le b+y$ (否则对子不够拆)。则先让 $x=\min(\lceil \frac{c-a}{2} \rceil,b)$ , 每次让 $y$ 加一个,再让 $x$ 加一个,一直到 $x+y\geq\lceil \frac{c-a}{2} \rceil$ 。最后输出 $a+b+x+2y$ 即可。
赛时代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10;
int n,num[N];
signed main(){
	int _;cin>>_;
	while(_--){
		cin>>n;
		for(int i=1;i<=n;i++) cin>>num[i];
		sort(num+1,num+1+n);
		int one=0,two=0,three=0,ans=0;
		for(int i=1;i<=n;i++){
			three+=num[i]/3;
			if(num[i]%3==1) one++;
			if(num[i]%3==2) two++;
		}
		if(one>=three){
			cout<<one+two<<endl;
			continue ;
		}
		int k=three-one;k=(k+1)/2;
		int y=min(k,two),x=0;//这里x与y和前面意思相反 
		if(y<k){
			k-=y;
			int kk=k/2;
			x+=kk,y+=kk;
			if(k%2) x++;
		}
		cout<<one+two+x*2+y<<endl;
	}
	return 0;
}
                    
                
                
            
        
浙公网安备 33010602011771号