当前位置: 代码迷 >> 综合 >> 【NOIP2015】洛谷2668 斗地主
  详细解决方案

【NOIP2015】洛谷2668 斗地主

热度:94   发布时间:2024-01-13 11:42:59.0

题目描述

牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的A到K加上大小王的共54张牌来进行的扑克牌游戏。在斗地主中,牌的大小关系根据牌的数码表示如下:3<4<5<6<7<8<9<10< J< Q< K< A< 2<小王<大王,而花色并不对牌的大小产生影响。每一局游戏中,一副手牌由n张牌组成。游戏者每次可以根据规定的牌型进行出牌,首先打光自己的手牌一方取得游戏的胜利。

现在,牛牛只想知道,对于自己的若干组手牌,分别最少需要多少次出牌可以将它们打光。请你帮他解决这个问题。

需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。

具体规则如下:

输入输出格式 输入格式:

第一行包含用空格隔开的2个正整数Tn,表示手牌的组数以及每组手牌的张数。

接下来T组数据,每组数据n行,每行一个非负整数对aibi表示一张牌,其中ai示牌的数码,bi表示牌的花色,中间用空格隔开。特别的,我们用1来表示数码A,11表示数码J,12表示数码Q,13表示数码K;黑桃、红心、梅花、方片分别用1-4来表示;小王的表示方法为01,大王的表示方法为02。

输出格式:

共T行,每行一个整数,表示打光第i手牌的最少次数。

可以把所有的出法分成两种,顺子和单独出的。
注意到出牌的顺序是没有关系的,所以可以人为规定顺序。
先暴力搜索顺子,然后可以对每种状态简单地计算出剩下的不出顺子的出法。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int oo=0x3f3f3f3f;
int num[20],cnt[5],n,ans;
void fin(int now)
{memset(cnt,0,sizeof(cnt));for (int i=3;i<=16;i++)if (num[i]){if (num[i]<3) cnt[2]++;else cnt[num[i]]++;}ans=min(ans,now+cnt[3]+cnt[4]+max(cnt[2]-cnt[3]-cnt[4]*2,0));
}
void dfs(int now)
{fin(now);for (register int i=3;i<=14;i++)if (num[i])for (register int j=i+1;j<=14&&num[j];j++)if (j-i+1>=5){for (register int k=i;k<=j;k++)num[k]--;dfs(now+1);for (register int k=i;k<=j;k++)num[k]++;}for (register int i=3;i<=14;i++)if (num[i]>=2)for (register int j=i+1;j<=14&&num[j]>=2;j++)if (j-i+1>=3){for (register int k=i;k<=j;k++)num[k]-=2;dfs(now+1);for (register int k=i;k<=j;k++)num[k]+=2;}for (register int i=3;i<=14;i++)if (num[i]>=3)for (register int j=i+1;j<=14&&num[j]>=3;j++){for (register int k=i;k<=j;k++)num[k]-=3;dfs(now+1);for (register int k=i;k<=j;k++)num[k]+=3;}
}
int main()
{int i,j,k,x,y,z,T;scanf("%d%d",&T,&n);while (T--){memset(num,0,sizeof(num));for (i=1;i<=n;i++){scanf("%d%d",&x,&y);if (x==1) num[14]++;if (x==2) num[15]++;if (x==0) num[16]++;if (x>=3) num[x]++;}ans=oo;dfs(0);printf("%d\n",ans);}
}