题目描述 Description
牛牛最近迷上了一种叫斗地主的扑克游戏。 斗地主是一种使用黑桃、红心、梅花、
方片的 A 到 K 加上大小王的共 54 张牌来进行的扑克牌游戏。在斗地主中, 牌的大小关
系根据牌的数码表示如下: 3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王, 而花色并不
对牌的大小产生影响。 每一局游戏中,一副手牌由 n 张牌组成。游戏者每次可以根据规
定的牌型进行出牌, 首先打光自己的手牌一方取得游戏的胜利。
现在,牛牛只想知道,对于自己的若干组手牌, 分别最少需要多少次出牌可以将它
们打光。 请你帮他解决这个问题。
需要注意的是, 本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。
具体规则如下:
输入描述 Input Description
输入文件名为 landlords.in。
第一行包含用空格隔开的 2 个正整数T,n,表示手牌的组数以及每组手牌的张数。
接下来T组数据,每组数据n行, 每行一个非负整数ai,bi,对表示一张牌, 其中ai表
示牌的数码,bi表示牌的花色,中间用空格隔开。 特别的, 我们用1 来表示数码 A, 11 表
示数码 J, 12 表示数码 Q, 13 表示数码 K;黑桃、红心、梅花、方片分别用 1-4 来表示; 小
王的表示方法为 0 1, 大王的表示方法为 0 2。
输出描述 Output Description
输出文件名为 landlords.out。
共 T 行,每行一个整数,表示打光第i组手牌的最少次数。
样例输入 Sample Input
输入样例1
1 8
7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1
—————
输入样例2
1 17
12 3
4 3
2 3
5 4
10 2
3 3
12 2
0 1
1 3
10 1
6 2
12 1
11 3
5 2
12 4
2 2
7 2
样例输出 Sample Output
输出样例1
3
—————
输出样例2
6
数据范围及提示 Data Size & Hint
测试点, 我们约定手牌组数 与张数 的规模如下:
测试点编号 T n
1 100 2 |11 100 14
2 100 2 |12 100 15
3 100 3 |13 10 16
4 100 3 |14 10 17
5 100 4 |15 10 18
6 100 4 |16 10 19
7 100 10 |17 10 20
8 100 11 |18 10 21
9 100 12 |19 10 22
#include<cstdio> #include<cstring> #include<iostream> #define M 18 using namespace std; int n,x[M],cnt[5],ans; int read() { char c=getchar();int num=0; while(c<‘0‘||c>‘9‘){c=getchar();} while(c>=‘0‘&&c<=‘9‘){num=num*10+c-‘0‘;c=getchar();} return num; } int calc()//先把能带着出的都出上 { memset(cnt,0,sizeof(cnt)); int res=0; for(int i=0;i<M;i++)cnt[x[i]]++; //cnt[i]表示出现i次的数有几个 while(cnt[4]&&cnt[2]>=2) res++,cnt[4]--,cnt[2]-=2; //四带两对 while(cnt[4]&&cnt[1]>=2) res++,cnt[4]--,cnt[1]-=2; //四带二 while(cnt[3]&&cnt[2]>=1) res++,cnt[3]--,cnt[2]-=1; //三带一对 while(cnt[3]&&cnt[1]>=1) res++,cnt[3]--,cnt[1]-=1; //三带一 return res+cnt[1]+cnt[2]+cnt[3]+cnt[4]; //统计出当前答案 } inline void dfs(int step) { if(step>ans) return; //每更新一个状态之前,判断当前状态先出“n带m”是否更优 ans=min(ans,step+calc()); //三顺子 for(int i=2;i<=M;i++)//最小从3开始 { int j; for(j=i;x[j]>=3;j++);//可以确保 i~j-1 之间所有数字的数量都>=3 if(j-i>=2)//至少连续2个 { for(int t=j;t-i>=2;t--)//从j枚举结束位置 { for(int k=i;k<t;k++) x[k]-=3; dfs(step+1); for(int k=i;k<t;k++) x[k]+=3;//回溯 } } } //双顺子 for(int i=2;i<=M;i++) { int j; for(j=i;x[j]>=2;j++); if(j-i>=3) { for(int t=j;t-i>=3;t--) { for(int k=i;k<t;k++) x[k]-=2; dfs(step+1); for(int k=i;k<t;k++) x[k]+=2; } } } //单顺子 for(int i=2;i<=M;i++) { int j; for(j=i;x[j]>=1;j++); if(j-i>=5) { for(int t=j;t-i>=5;t--) { for(int k=i;k<t;k++) x[k]-=1; dfs(step+1); for(int k=i;k<t;k++) x[k]+=1; } } } } int main() { int T=read(),n=read(); while(T--) { memset(x,0,sizeof(x)); for(int i=1;i<=n;i++) { int w=read(),c=read(); if(w==1) w=13;//把A转成13,其余数字减1 else if(w) w--; x[w]++; //统计每个数字的个数 } ans=calc(); dfs(0); printf("%d\n", ans); } return 0; }