题解 P2668 【斗地主】
钱逸凡
2018-07-12 12:49:14
~~虽然本题是搜索,却感觉和做模拟差不多,情况很多,代码很长,打起来很烦。~~
### 首先要明确:先打顺子和四带二和三带一,最后打单牌
至于那个三连对,其实是没有用的,因为如果我们有三张牌是一样的,我们可以用来打三带一,打三连对实在是太浪费了。
### 其次:顺子并不是越长越好
为什么呢?
~~打过斗地主的可以跳过解说直接看代码~~
在我们打顺子时可能会拆散本可以打三带一和四带二的牌,导致出了更多次牌。
举个例子:3 ,4 ,5, 6, 7, 8, 9, 10, 10, 10,A(1)
如果我们打的顺子是3,4,5,6,7,8,9,10,接下来还要出两次:10,10(对子)和A。一共三次。
但如果我们的顺子是3,4,5,6,7,8,9,接下来只要出一次:10,10,10,A(三带一)。一共两次。
小细节,1(A)可以接在13后面,但是2不可以接在1后面,也不可以接在3前面。
综上所述,得出代码:
```
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int T,n,c[14],ans;
inline int Read(){
int x=0;
char c=getchar();
while(c>'9'||c<'0')c=getchar();
while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
return x;
}
inline int min(int a,int b){
return a<b?a:b;
}
void init(){
register int i;
for(i=0;i<=13;i++)c[i]=0;
int a,b;
for(i=1;i<=n;i++){
a=Read(),b=Read();
c[a]++;//花色不重要
}
ans=14;
}
inline int four(){
for(int i=0;i<=13;i++){
if(c[i]==4)return i;
}
return -1;
}//是否有四张牌相同
inline int three(){
for(int i=0;i<=13;i++){
if(c[i]>=3)return i;
}
return -1;
}//是否有三张牌相同
inline bool shun(int i){
if(c[i]>=1&&
c[i+1]>=1&&
c[i+2]>=1&&
c[i+3]>=1&&
c[(i+3)%13+1]>=1)return 1;
return 0;
}//是否有顺子
inline bool dshun(int i){
if(c[i]>=2&&
c[i+1]>=2&&
c[(i+1)%13+1]>=2)return 1;
return 0;
}//是否有连对
void dfs(int deep,int leave){//打了几次,还剩几张
if(deep>=ans)return;
if(leave==0){
ans=deep;
return;
}
int k,kk,i;
//打顺子
for(i=3;i<=10;i++){
if(shun(i)){
k=i;
kk=k;
while(c[k]>=1&&k!=2){
c[k]--;
k++;
if(k-kk>=5)dfs(deep+1,leave-k+kk);//顺子并不是越长越好
if(k==14)k=1;
}//把顺子打出
if(k==1)k=14;
if(k==2)k=15;
dfs(deep+1,leave-k+kk);
if(k==15)c[1]++,k--;
k--;
for(;kk<=k;kk++)c[kk]++;//回溯
}
}
//打连对
for(i=3;i<=12;i++){
if(dshun(i)){
k=i;
kk=k;
while(c[k]>=2&&k!=2){
c[k]-=2;
k++;
if(k-kk>=3)dfs(deep+1,leave-2*k+2*kk);//同样,连对也不是越长越好
if(k==14)k=1;
}//把连对打出
if(k==1)k=14;
if(k==2)k=15;
dfs(deep+1,leave-2*k+2*kk);
if(k==15)c[1]+=2,k--;
k--;
for(;kk<=k;kk++)c[kk]+=2;//回溯
}
}
//打4带2
k=four();
if(k!=-1){
c[k]-=4;
for(int i=0;i<=13;i++){
if(c[i]==0)continue;
for(int j=0;j<=13;j++){
if(c[j]==0)continue;
if(j==i)continue;
if(c[i]>=2&&c[j]>=2){
c[i]-=2;
c[j]-=2;
dfs(deep+1,leave-8);
c[i]+=2;
c[j]+=2;
}//四带两对
if(c[i]>=1&&c[j]>=1){
c[i]-=1;
c[j]-=1;
dfs(deep+1,leave-6);
c[i]++;
c[j]++;
}//四带二
}
}
dfs(deep+1,leave-4);//打炸弹
c[k]+=4;
}
//打三带一(对)
k=three();
if(k!=-1){
c[k]-=3;
for(int i=0;i<=13;i++){
if(c[i]>=1){
c[i]--;
dfs(deep+1,leave-4);
c[i]++;
}//三带一
if(c[i]>=2){
c[i]-=2;
dfs(deep+1,leave-5);
c[i]+=2;
}//三带一对
}
dfs(deep+1,leave-3);//打三张单牌
c[k]+=3;
}
////剩下的全部打单牌和对子
int s=0;
for(int i=0;i<=13;i++){
if(c[i]>=1){
s++;
}
}
dfs(deep+s,0);
}
int main(){
T=Read();n=Read();
while(T--){
init();
dfs(0,n);
printf("%d\n",ans);
}
return 0;
}
```