山东济南彤昌机械科技有限公司 山东济南江鹏工贸游有限公司

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 }

 

posted on 2016-02-25 15:59  hahalidaxin  阅读(2678)  评论(0编辑  收藏  举报