NOIP2015 斗地主(搜索+剪枝)


 

4325: NOIP2015 斗地主

Time Limit: 30 Sec  Memory Limit: 1024 MB
Submit: 270  Solved: 192
[Submit][Status][Discuss]

Description

 牛牛最近迷上了一種叫斗地主的撲克游戲。斗地主是一種使用黑桃、紅心、梅花、方片的A到K加上大小王的共54張牌來進行的撲克牌游戲。在斗地主中,牌的大小關系根據牌的數碼表示如下:3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色並不對牌的大小產生影響。每一局游戲中,一副手牌由n張牌組成。游戲者每次可以根據規定的牌型進行出牌,首先打光自己的手牌一方取得游戲的勝利。現在,牛牛只想知道,對於自己的若干組手牌,分別最少需要多少次出牌可以將它們打光。請你幫他解決這個問題。需要注意的是,本題中游戲者每次可以出手的牌型與一般的斗地主相似而略有不同。具體規則如下:

Input

第一行包含用空格隔開的2個正整數T,N,表示手牌的組數以及每組手牌的張數。

接下來T組數據,每組數據N行,每行一個非負整數對Ai,Bi,表示一張牌,其中Ai表示牌的數碼,Bi表示牌的花色,中間用空格隔開。特別的,我們用1來表示數碼A,11表示數碼J,12表示數碼Q,13表示數碼K;黑桃、紅心、梅花、方片分別用1-4來表示;小王的表示方法為01,大王的表示方法為02。
 

Output

共T行,每行一個整數,表示打光第T組手牌的最少次數。

Sample Input

1 8
7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1

Sample Output

3

HINT

 

 共有1組手牌,包含8張牌:方片7,方片8,黑桃9,方片10,黑桃J,黑桃5,方


片A以及黑桃A。可以通過打單順子(方片7,方片8,黑桃9,方片10,黑桃J),單張

牌(黑桃5)以及對子牌(黑桃A以及方片A)在3次內打光。

 

T<=10

N<=23

 

【思路】

 

       搜索+剪枝

       忽略花色,統計每種碼數出現次數方便出牌。

       每次都先出順子,對於手中剩下的牌我們貪心地將剩下的組合牌需要打的次數計算出來,然后更新ans以剪枝。

       雙王算作對牌。順排不包括2和雙王。

 

【代碼】

 1 #include<cstdio>
 2 #include<cstring>
 3 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 4 using namespace std;
 5 
 6 const int N = 25;
 7 
 8 int a[N],c[N];
 9 int n,T,ans;
10 
11 int Qans() {
12     memset(c,0,sizeof(c));
13     FOR(i,0,13) c[a[i]]++;
14     int tot=0;                                    //tot帶牌 
15     while(c[4]&&c[2]>1)  c[4]--,c[2]-=2,tot++;      
16     while(c[4]&&c[1]>1) c[4]--,c[1]-=2,tot++;
17     while(c[4]&&c[2]) c[4]--,c[2]--,tot++;
18     while(c[3]&&c[2]) c[3]--,c[2]--,tot++;
19     while(c[3]&&c[1]) c[3]--,c[1]--,tot++;
20     return tot+c[1]+c[2]+c[3]+c[4];                //帶牌+三張 對子 單張 
21 }
22 
23 void dfs(int now) {
24     if(now>=ans) return ;
25     int tmp=Qans();
26     if(now+tmp<ans)  ans=now+tmp;
27     FOR(i,2,13) {                                //三順子 
28         int j=i;
29         while(a[j]>=3) j++;
30         if(j-i>=2) {
31             FOR(j2,i+1,j-1) {
32                 FOR(k,i,j2) a[k]-=3;
33                 dfs(now+1);
34                 FOR(k,i,j2) a[k]+=3;
35             }
36         }
37     }
38     FOR(i,2,13) {                                //雙順子 
39         int j=i;
40         while(a[j]>=2) j++;
41         if(j-i>=3) {
42             FOR(j2,i+2,j-1) {
43                 FOR(k,i,j2) a[k]-=2;
44                 dfs(now+1);
45                 FOR(k,i,j2) a[k]+=2;
46             }
47         }
48     }
49     FOR(i,2,13) {                                //單順子 
50         int j=i;
51         while(a[j]>=1) j++;
52         if(j-i>=5) {
53             FOR(j2,i+4,j-1) {
54                 FOR(k,i,j2) a[k]--;
55                 dfs(now+1);
56                 FOR(k,i,j2) a[k]++;
57             }
58         }
59     }
60 }
61 
62 int main() {
63     //freopen("in.in","r",stdin);
64     //freopen("out.out","w",stdout);
65     scanf("%d%d",&T,&n);
66     while(T--) {
67         memset(a,0,sizeof(a));
68         int x,y;
69         FOR(i,1,n) {
70             scanf("%d%d",&x,&y);
71             if(x==1) x=13; else if(x) x--;
72             a[x]++;
73         }
74         ans=1e9;
75         dfs(0);
76         printf("%d\n",ans);
77     }
78     return 0;
79 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM