【NOIP 2015】斗地主
题目描述
牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的 A 到 K 加上大小王的共 54 张牌来进行的扑克牌游戏。在斗地主中,牌的大小关 系根据牌的数码表示如下: 3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色并不对牌的大小产生影响。每一局游戏中,一副手牌由 n 张牌组成。游戏者每次可以根据规定的牌型进行出牌,首先打光自己的手牌一方取得游戏的胜利。
现在,牛牛只想知道,对于自己的若干组手牌,分别最少需要多少次出牌可以将它们打光。请你帮他解决这个问题。
需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。具体规则如下:
输入输出格式
输入格式:
第一行包含用空格隔开的2个正整数 T,n ,表示手牌的组数以及每组手牌的张数。
接下来 T 组数据,每组数据 n 行,每行一个非负整数对 ai,bi ,表示一张牌,其中 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
题解
爆搜+大模拟:
有人会问:如何爆搜?
并不是真的纯爆搜,只是爆搜顺子,其余的牌贪心去打,注意顺序(代码里有解释);
此题也有一些坑,比如说‘2’不能放在顺子里(我被坑了很久,很少斗地主),这也说明学OI也是需要一些生活经验的;
code:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> #define ll long long using namespace std; int read(){ int X=0,w=0; char ch=0; while(!isdigit(ch)) {w|=ch=='-';ch=getchar();} while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } int t,n,ans,ord,x,sum; int cnt[200],a[100]; //下面函数都是return 以第i张牌为首能打出顺的最大长度 int danshunzi(int i){ int p=i; while(cnt>=1&&p<=13)p++; if(p-i>=5)return p; return 0; } int shuangshunzi(int i){ int p=i; while(cnt[p]>=2&&p<=13)p++; if(p-i>=3)return p; return 0; } int sanshunzi(int i){ int p=i; while(cnt[p]>=3&&p<=13)p++; if(p-i>=2)return p; return 0; } //打完顺子以后贪心打其他牌的最小次数 //第2个while和第3个while顺序不能换(虽然打出牌的数量一样) //因为要是单独打单的话需要用两次,而打对需要用一次; //所以贪心带单 int calc(){ int tot=0; memset(a,0,sizeof(a)); for(int i=0;i<=13;i++)a[cnt[i]]++;//桶套桶 ^-^ while(a[4] && a[2]>1) a[4]--,a[2]-=2,tot++;// 8 while(a[4] && a[1]>1) a[4]--,a[1]-=2,tot++;// 6 while(a[4] && a[2]) a[4]--,a[2]--,tot++;// 6 while(a[3] && a[2]) a[3]--,a[2]--,tot++;// 5 while(a[3] && a[1]) a[3]--,a[1]--,tot++;// 5 return tot+a[1]+a[2]+a[3]+a[4]; } void dfs(int dep){ if(dep>=ans) return; int k=calc(); ans=min(ans,dep+k); int pos; for(int i=2;i<=13;i++){ if(sanshunzi(i)){ //三顺子 6+ pos=sanshunzi(i); for(int v=i+1;v<=pos-1;v++){ //枚举长度 for(int j=i;j<=v;j++) cnt[j]-=3; dfs(dep+1); for(int j=i;j<=v;j++) cnt[j]+=3; } } } for(int i=2;i<=13;i++){ if(shuangshunzi(i)){ //双顺子 6+ pos=shuangshunzi(i); for(int v=i+2;v<=pos-1;v++){ for(int j=i;j<=v;j++) cnt[j]-=2; dfs(dep+1); for(int j=i;j<=v;j++) cnt[j]+=2; } } } for(int i=2;i<=13;i++){ if(danshunzi(i)){ //单顺子 5+ pos=danshunzi(i); for(int v=i+4;v<=pos-1;v++){ for(int j=i;j<=v;j++) cnt[j]--; dfs(dep+1); for(int j=i;j<=v;j++) cnt[j]++; } } } } int main() { freopen("cards.in","r",stdin); freopen("cards.out","w",stdout); t=read();n=read(); while(t--){ ans=30; memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++){ x=read();ord=read(); if(x==1)x=13; // A else if(x) x--; // 1对应2 2对应3 ....... cnt[x]++; // 这样存可以解决2不能在顺子里的坑 } dfs(0); printf("%d\n",ans); } return 0; }
我觉得我该斗两把地主了 ^-^[p]
- [BZOJ4325][NOIP2015]斗地主(dfs)
- 2106. [NOIP2015] 斗地主
- NOIP2015 斗地主 洛谷P2668
- Noip2015 斗地主【搜索】【贪心】
- UOJ 147|NOIP 2015|斗地主|搜索|贪心
- 【NOIP2015 DAY1 T3 】斗地主(landlords)
- 洛谷P2668 斗地主 [NOIP2015]
- [BZOJ4325]NOIP2015 斗地主
- NOIP2015 斗地主 解题报告(搜索)
- [BZOJ 4325][NOIP 2015] 斗地主
- (noip 2015 斗地主)<搜索+贪心>
- [noip2015]斗地主
- NOIP 2015 提高组 Day1 斗地主
- 【NOIP2015】斗地主
- [noip2015]斗地主(dfs+贪心)
- NOIP 2015 D1 T3 斗地主(特详细讲解)
- C++——NOIP2015提高组day1 t3——斗地主
- Noip2015 Day1 T3 斗地主(Dfs+Dp优化)
- 【uoj147】NOIP2015—斗地主
- Luogu 2668 NOIP 2015 斗地主(搜索,动态规划)