题目描述
牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的A到K加上大小王的共54张牌来进行的扑克牌游戏。在斗地主中,牌的大小关系根据牌的数码表示如下:3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色并不对牌的大小产生影响。每一局游戏中,一副手牌由n张牌组成。游戏者每次可以根据规定的牌型进行出牌,首先打光自己的手牌一方取得游戏的胜利。
现在,牛牛只想知道,对于自己的若干组手牌,分别最少需要多少次出牌可以将它们打光。请你帮他解决这个问题。
需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。
具体规则如下:
输入输出格式
输入格式:
第一行包含用空格隔开的2个正整数T和n,表示手牌的组数以及每组手牌的张数。
接下来T组数据,每组数据n行,每行一个非负整数对aibi表示一张牌,其中ai示牌的数码,bi表示牌的花色,中间用空格隔开。特别的,我们用1来表示数码A,11表示数码J,12表示数码Q,13表示数码K;黑桃、红心、梅花、方片分别用1-4来表示;小王的表示方法为01,大王的表示方法为02。
输出格式:
共T行,每行一个整数,表示打光第i手牌的最少次数。
输入输出样例
输入样例#1:
1 8
7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1
输出样例#1:
3
输入样例#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
输出样例#2:
6
说明
样例1说明
共有1组手牌,包含8张牌:方片7,方片8,黑桃9,方片10,黑桃J,黑桃5,方片A以及黑桃A。可以通过打单顺子(方片7,方片8,黑桃9,方片10,黑桃J),单张牌(黑桃5)以及对子牌(黑桃A以及方片A)在3次内打光。
对于不同的测试点, 我们约定手牌组数T与张数n的规模如下:
数据保证:所有的手牌都是随机生成的。
dp+深搜版
1 8
3 1
3 2
3 3
5 1
5 2
5 3
8 1
8 2
8 3
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN=;
int T,n,p,hs,ans;
int dp[MAXN][MAXN][MAXN][MAXN],card_num[MAXN],happen[MAXN/];
int take_num[]={,,,};
int read(int & n)
{
char c='-';int x=;
while(c<''||c>'')c=getchar();
while(c>=''&&c<='')
{
x=x*+(c-);
c=getchar();
}
n=x;
}
int calc(int one,int two,int three,int four,int king)
{
if(king==)// 只有一张大小王
{
one++;// 看做单牌处理
king=;
}
if(king==)
return dp[four][three][two][one];
else
return min(dp[four][three][two][one+],dp[four][three][two][one]+);
}
void dfs(int now)//now是指已经操作的次数
{
if(now>ans)
return ;
memset(happen,,sizeof(happen));// 初始化
for(int i=;i<=;i++)
happen[card_num[i]]++;
ans=min(ans,now+calc(happen[],happen[],happen[],happen[],card_num[]));
for(int k=;k<=;k++)// 顺子
{
for(int i=;i<=;i++)
{
int j;
for(j=i;j<=&&card_num[j]>=k;j++)
{
card_num[j]-=k;
if(j-i+>=take_num[k])
dfs(now+);
}
for(j--;j>=i;j--)
card_num[j]+=k;
}
}
}
int main()
{
// freopen("landlords.in","r",stdin);
// freopen("landlords.out","w",stdout);
read(T);read(n);
memset(dp,,sizeof dp);
dp[][][][]=;
// dp[i][j][k][l]表示打出i套四张,j套三张,k套两站,l张单牌所需要的最少步数
for(int i=;i<=n;i++)//四张
for(int j=;j<=n;j++)//三张
for(int k=;k<=n;k++)//两张
for(int l=;l<=n;l++)//一张
if(i*+j*+k*+l*<=n)
{
dp[i][j][k][l]=i+j+k+l;//最坏的情况
if(i)
{
if(k>=)
dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-][j][k-][l]+);
// 四带一对对牌
if(l>=)
dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-][j][k][l-]+);
// 一对单牌
dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-][j][k][l]+);
//啥都不带
}
if(j)
{
if(k)
dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-][k-][l]+);
// 3带对
if(l)
dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-][k][l-]+);
// 3带单
dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-][k][l]+);
// 什么都不带
}
if(k)
dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j][k-][l]+);
if(l)
dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j][k][l-]+);
}
while(T--)
{
memset(card_num,,sizeof(card_num));// 初始化
ans=n;
for(int i=;i<=n;i++)
{
read(p);read(hs);
if(p==)
card_num[]++;//大小王
else if(p==)
card_num[]++;// A
else card_num[p]++;
}
dfs();
printf("%d\n",ans);
}
return ;
}