NOIp2015D1T3 斗地主【暴搜】

题目传送门

刚开始读到题目的时候,非常懵逼,非常崩溃,写着写着呢,也有点崩溃,细节有点多。

这个做法呢,只能过掉官方数据,洛谷上好像有加强版,只能得$86$分,就没有管了。

大概说一下思路:

暴力搜索每一种可能的情况,如果可以就递归下去,然后回溯。

搜索框架的话,大概就是把当前搜到的出牌次数传到参数里面,如果参数已经大于了当前的最小答案就剪枝退出。然后在当前状态下枚举每一种出牌的情况,能够出就出,然后递归到下一层,参数出牌次数+1。

可以证明不会无限递归下去,因为在参数已经大于了当前的最小答案的时候就退出了。

可以分为三大类出牌:顺子、带牌、散牌

还有一些细节什么的:

1.把$A$放在$K$后面,顺子的时候比较方便

2.$2$不能在顺子里面(当时还理解了好久$2$点是什么,还以为是什么其他代称...)

3.王。之前以为王只可以单出或者火箭,没有认真读题,王是可以被带的,但是只能带单牌,王不能看成一对,他们牌值不一样。

4.后来发现顺子和带牌打完之后,可以不用单独考虑打散牌(单牌,对子,三张牌),因为如果剩下,$1$次就可以打出去一种,而且无论如何只打$1$次用的打牌次数会最少。而且散牌不用考虑与其他牌的组合。(可以感性理解,次数最多肯定是每一次都按种数打散牌)

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<vector>
  4 #include<cstring>
  5 #include<queue>
  6 #include<map>
  7 #include<iostream>
  8 using namespace std;
  9 #define ll long long
 10 #define INF 0x3f3f3f3f
 11 #define N 30
 12 int rd()
 13 {
 14     int f=1,s=0;char c=getchar();
 15     while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
 16     while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+(c^48);c=getchar();}
 17     return f*s;
 18 }
 19 //J->11 Q->12 K->13 A->14 2->15 King->16 
 20 //王特殊处理 只能出火箭或者单出(糟糕 还可以单带 
 21 //2不能出顺子 
 22 int n,ans;
 23 int cnt[N];
 24 void dfs(int res)
 25 {
 26     if(res>=ans) return ;//剪枝 提前退出
 27     /*
 28     //-----炸弹
 29     for(int i=3;i<=15;i++)
 30         if(cnt[i]>=4)
 31         {
 32             cnt[i]-=4;
 33             dfs(res+1);
 34             cnt[i]+=4;
 35         }
 36     //----- 
 37     //-----对子牌
 38     for(int i=3;i<=15;i++)
 39         if(cnt[i]>=2)
 40         {//2个对子...就是炸弹了 应该在前面判断过了 
 41             cnt[i]-=2;
 42             dfs(res+1);
 43             cnt[i]+=2;
 44          } 
 45     //----- 
 46     //-----三张牌 
 47     for(int i=3;i<=15;i++)
 48         if(cnt[i]>=3)
 49         {
 50             cnt[i]-=3;
 51             dfs(res+1);
 52             cnt[i]+=3;
 53          }
 54     //----- 
 55     */
 56     //-----顺子
 57     //---单顺子 
 58     int k=0;
 59     for(int i=3;i<=14;i++)//不包括2点 
 60     {
 61         if(cnt[i]==0) k=0;//顺子断了 从这里重新开始
 62         else//否则就接下去
 63         {
 64             k++;
 65             if(k>=5)
 66             {
 67                 for(int j=i;j>=i-k+1;j--)
 68                     cnt[j]--;
 69                 dfs(res+1);
 70                 for(int j=i;j>=i-k+1;j--)
 71                     cnt[j]++;
 72             }
 73         }
 74     } 
 75     //--- 
 76     //---双顺子 
 77     k=0;
 78     for(int i=3;i<=14;i++)//不包括2点
 79     {
 80         if(cnt[i]<2) k=0;//顺子断了 从这里重新开始
 81         else//否则就接下去 
 82         {
 83             k++;
 84             if(k>=3)
 85             {
 86                 for(int j=i;j>=i-k+1;j--)
 87                     cnt[j]-=2;
 88                 dfs(res+1);
 89                 for(int j=i;j>=i-k+1;j--)
 90                     cnt[j]+=2;
 91             }
 92         }
 93     }
 94     //---
 95     //---三顺子
 96     k=0;
 97     for(int i=3;i<=14;i++)//不包括2点
 98     {
 99         if(cnt[i]<3) k=0;//顺子断了 从这里重新开始
100         else//否则就接下去 
101         {
102             k++;
103             if(k>=2)
104             {
105                 for(int j=i;j>=i-k+1;j--)
106                     cnt[j]-=3;
107                 dfs(res+1);
108                 for(int j=i;j>=i-k+1;j--)
109                     cnt[j]+=3;
110             }
111         }
112     }
113     //--- 
114     //-----
115     //-----带牌
116     for(int i=3;i<=15;i++)
117     {
118         if(cnt[i]<3) continue;
119         //3带X 
120         cnt[i]-=3;
121         for(int j=3;j<=16;j++)
122         {
123             if(i==j) continue;
124             if(cnt[j]>=1)
125             {
126                 cnt[j]--;
127                 dfs(res+1);
128                 cnt[j]++;
129             }
130             if(cnt[j]>=2&&j!=16/*王不能一起带*/)
131             {
132                 cnt[j]-=2;
133                 dfs(res+1);
134                 cnt[j]+=2;
135             }
136         }
137         cnt[i]+=3;
138         //4带X(两个单张或两个对)
139         if(cnt[i]<4) continue;
140         cnt[i]-=4;
141         for(int j=3;j<=16;j++)
142         {
143             if(i==j||cnt[j]<=0) continue;
144             cnt[j]--;//两张单牌 
145             for(int k=3;k<=16;k++)
146             {
147                 if(k==j||k==i||cnt[k]<1) continue;
148                 cnt[k]--;
149                 dfs(res+1);
150                 cnt[k]++; 
151             }
152             cnt[j]++;
153             if(cnt[j]<2||j==16/*王不能一起*/) continue; 
154             cnt[j]-=2;//两对对子
155             for(int k=3;k<=15;k++)
156             {
157                 if(k==j||k==i||cnt[k]<2) continue;
158                 cnt[k]-=2;
159                 dfs(res+1);
160                 cnt[k]+=2;
161             } 
162             cnt[j]+=2;
163         }
164         cnt[i]+=4;
165      }
166     //----- 
167     for(int i=3;i<=16;i++)
168         if(cnt[i]) res++;
169     //突然发现 单张 对子三张都不用特殊考虑 反正能一次出完剩下的所有牌 
170     ans=min(ans,res);
171 }
172 int main() 
173 {
174     int T=rd(),n=rd();
175     while(T--)
176     {
177         memset(cnt,0,sizeof(cnt));
178         ans=INF;
179         for(int i=1;i<=n;i++)
180         {
181             int m=rd(),opt=rd();
182             if(m==0) m=16;
183             if(m==1) m=14;
184             if(m==2) m=15;
185             cnt[m]++;
186         }
187         dfs(0);
188         printf("%d
",ans);
189     }
190     return 0;
191 }
Code
原文地址:https://www.cnblogs.com/lyttt/p/11832122.html