题解:P11323 【MX-S7-T1】「SMOI-R2」Happy Card
Tenil
·
·
题解
Solution
闲话
蒟蒻第一次写题解,若有不当还请海涵。
题意
## 分析
逐类考虑:
首先,某类牌多于4张时无脑炸不是最优(本蒟蒻在这里先死了10分钟),只要看一眼样例的例一不难得出——
>[1,1,1,1][1][2]:3步
>
>
>[1,1,1,2][1,1]:2步
因此考虑拆掉:2+2完全亏,因此全部拆为1+3.
可以发现,三带一的方式是比较特殊的。容易想到:如果有能匹配的不匹配,则会造成2步浪费。故一定要优先匹配。但仅单牌和三个匹配可能有三个的数量多的情况,再考虑对子;
由拆炸为三带一自然想到对子可以拆成两个单牌:有三张相同和一个对子剩余时,拆2步,不拆3步,所以必拆补齐。(当然,无脑拆显然亏)
最后要是还有三个相同的剩余,考虑互相补足:只有$1\times3$直接出(2步),$2\times3$拆成三带一加一对(2步),$3\times3$为两个三带一加一张单牌,(3步)$4\times3$则为三个三带一(3步)。$4\times3$直接拆完,因此多于$4\times3$的情况$(4n+r)\times3$步数为$3n$加上$r\times3$的步数。
于是此题得解。
## 实现
读入每类牌张数,直接全拆成3张的。
按$cnt_1$+$cnt_2\times2$、$cnt_1$与$cnt_3$的大小写$if$条件即可。
详情请见代码——
## Code
```cpp
#include <iostream>
#include <cstdio>
#include <cctype>
using namespace std;
typedef long long ll;//开long long好习惯
inline ll fr() {
ll x=0; char c=getchar();
while(!isdigit(c)) c=getchar();//听说用isdigit()比char比大小快
while(isdigit(c)) {
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x;
}
const int maxn=1e6;
ll T,n,v,ans;
//T-数据组数 n-牌种数 v-当前牌种张数 ans-answer
int main() {
T=fr();
while(T--) {
n=fr();
ans=0;
ll cnt[4]={0,0,0,0};//张数为下标的牌堆组数
//多测不清空,直接见祖宗
for(register int i = 0; i < n; i++) {
v=fr();
cnt[3]+=v/3;
cnt[v%3]++;//直接分3
}
ll s=cnt[1]+2*cnt[2];//能分出1的数量
if(s>=cnt[3]) {//1多
if(cnt[1]>=cnt[3]) ans=cnt[1]+cnt[2];//单牌就够,对子直接出
else {
ans+=cnt[1];
cnt[3]-=cnt[1];//先用单牌填
ans+=cnt[3];
cnt[2]-=cnt[3]/2;//再用对子
ans+=cnt[2];//对子直接出
}
}
else {//3多
cnt[3]-=s;//能匹配的先匹配
ans=s+cnt[3]/4*3;//4x3的先考虑
cnt[3]%=4;
if(cnt[3]%3) ans+=2;//r=1,2时%3都非0
else if(cnt[3]) ans+=3;//为0时可能就是0
}
printf("%lld\n",ans);
//华丽输出
}
return 0;
}
```
## 后话
请切记:一拍脑子出的结论一定要验算一下(如本蒟蒻先丢完炸弹的愚蠢想法),不然拿着错结论代码写出花来都过不了。
如果有用还请点个赞!
审核求过啊!