题解:P2540 [NOIP2015 提高组] 斗地主 加强版
P2540 解题报告
前言
巨大抽象模拟搜索题。
写了一节自习课。
思路分析
首先因为一共就两副牌,可以直接搜索所有可能的情况。
然后就过了原题的数据了。
但是加强版数据有点强,这样写估计会 T 飞。
考虑发扬人类智慧。
如果你玩过斗地主的话,应该知道一些基本的策略。
比如说有顺子肯定不出单牌,四带二优先带单牌什么的。
总结一下我用的贪心策略:
-
优先考虑顺子;
-
多张数字相同的牌优先考虑在一起的情况,再考虑分开的情况;
-
四带二时优先考虑单牌,然后考虑两对,最后考虑一对;
-
三带一时优先考虑单牌,然后考虑一对。
所以实际上你可以先搜索所有顺子的情况,然后搜索所有的多张牌的情况,最后贪心地考虑散牌怎么出。
复杂度 。
代码实现
其实前面想明白了并不难写。
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int t,n,a,b,c[17],d[5],e[5],tot,ans,minn;
int getime(){
memcpy(e,d,sizeof(e));
int cnt=0;
while(d[4]){
if(d[1]>=2) d[1]-=2;
else if(d[2]>=2) d[2]-=2;
else if(d[2]>=1) d[2]--;
else break;
d[4]--;
cnt++;
}
while(d[3]){
if(d[1]) d[1]--;
else if(d[2]) d[2]--;
else break;
d[3]--;
cnt++;
}
cnt+=d[1]+d[2]+d[3]+d[4];
memcpy(d,e,sizeof(d));
return cnt;
}
void dfs2(){
tot=min(tot,getime());
if(d[4]){
d[4]--,d[3]++,d[1]++;
dfs2();
d[4]++,d[3]--,d[1]--;
d[4]--,d[2]++,d[2]++;
dfs2();
d[4]++,d[2]--,d[2]--;
}
if(d[3]){
d[3]--,d[2]++,d[1]++;
dfs2();
d[3]++,d[2]--,d[1]--;
}
if(d[2]){
d[2]--,d[1]+=2;
dfs2();
d[2]++,d[1]-=2;
}
}
void dfs1(int val){
memset(d,0,sizeof(d));
for(int i=2;i<=16;i++){
d[c[i]]++;
}
tot=inf;
dfs2();
ans=min(ans,val+tot);
for(int i=3;i<=14;i++){
if(!c[i]) continue;
for(int j=i+1;j<=14;j++){
if(!c[j]) break;
if(j-i+1<5) continue;
for(int k=i;k<=j;k++){
c[k]--;
}
dfs1(val+1);
for(int k=i;k<=j;k++){
c[k]++;
}
}
}
for(int i=3;i<=14;i++){
if(c[i]<2) continue;
for(int j=i+1;j<=14;j++){
if(c[j]<2) break;
if(j-i+1<3) continue;
for(int k=i;k<=j;k++){
c[k]-=2;
}
dfs1(val+1);
for(int k=i;k<=j;k++){
c[k]+=2;
}
}
}
for(int i=3;i<=14;i++){
if(c[i]<3) continue;
for(int j=i+1;j<=14;j++){
if(c[j]<3) break;
if(j-i+1<2) continue;
for(int k=i;k<=j;k++){
c[k]-=3;
}
dfs1(val+1);
for(int k=i;k<=j;k++){
c[k]+=3;
}
}
}
}
void solve(){
minn=inf;
int kin;
kin=min(c[15],c[16]);
c[15]-=kin;
c[16]-=kin;
ans=inf;
dfs1(0);
minn=min(minn,ans+kin);
c[15]+=kin;
c[16]+=kin;
ans=inf;
dfs1(0);
minn=min(minn,ans);
cout<<minn<<'\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t>>n;
while(t--){
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
tot=ans=minn=inf;
for(int i=1;i<=n;i++){
cin>>a>>b;
if(a==0 && b==1) c[15]++;
if(a==0 && b==2) c[16]++;
else if(a==1) c[14]++;
else c[a]++;
}
solve();
}
return 0;
}
后记
在考前锻炼一下码力还是有必要的。
本文作者:Kenma
本文链接:https://www.cnblogs.com/Kenma/p/18689793
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步