NOIP2015 斗地主(回溯)
2017-07-30 18:30
267 查看
题目描述
牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的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行,每行一个整数,表示打光第T组手牌的最少次数。
样例输入
1 8 7 4 8 4 9 1 10 4 11 1 5 1 1 4 1 1
样例输出
3
比较恶心的回溯...
花色无用,大小王可以当成两张相同的牌,只能单出或当对子.
首先注意顺子比带牌优,四带两对要比四带两单优,三带一对比三带一单优,判断时注意先后顺序.
过程分析:dfs模拟出牌,每次dfs首先统计如果当前出完需要多少步,更新ans;
solve函数:模拟带牌过程,统计如果将牌全部出完所需步数来更新ans;
dfs:模拟出顺子的过程。注意顺子不一定是最长就最优,比如当前的最长的顺子可能是在拆开另一对顺子或带牌时得到的,所以要枚举顺子的长度来保证回溯到所有结果,注意二点大小王不能用.
主体就这两部分,主要是顺子和带牌的处理,或许描述的不够清楚,具体细节参考代码注释.
#include<cstdio> #include<iostream> #include<cstring> #define maxn 16 #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; inline int read() { char c=getchar();int x=0,y=1; while(c<'0'||c>'9'){if(c=='-') y=-1;c=getchar();} while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); return x*y; } int t,n,ans,num[maxn],cnt,sz[maxn]; inline int st(int x)//为了后面计算方便,将3标为一号,1,2分别标为12,13号,大小王当成一张14号 { if(x>=3) return x-2; if(x==1) return 12; if(x==2) return 13; if(x==0) return 14; } int solve()//处理带牌 { int tmp=0;mem(sz,0);//注意清空数组 for(int i=1;i<=14;i++) sz[num[i]]++;//统计一定数量的牌有多少张,举个例子,如果整副牌里只有两个对子JJ,QQ,那么sz[2]=2; while(sz[4]&&sz[2]>=2) tmp++,sz[4]--,sz[2]-=2;//模拟四带两对的过程 while(sz[4]&&sz[1]>=2) tmp++,sz[4]--,sz[1]-=2;//模拟四带两单的过程 while(sz[3]&&sz[2]>=1) tmp++,sz[3]--,sz[2]--;//模拟三带一对的过程 while(sz[3]&&sz[1]>=1) tmp++,sz[3]--,sz[1]--;//模拟三带一单的过程 return tmp+sz[1]+sz[2]+sz[3]+sz[4];//剩下的对子和单牌依次出,所以tmp+sz[1]+sz[2]+sz[3]+sz[4]就是出完牌所需的步数 } void dfs() { if(cnt>ans) return; ans=min(ans,cnt+solve());//更新ans,cnt+返回值就是最终结果 for(int i=1;i<=11;i++)//模拟3顺子,注意边界 { int j=i; while(num[j]>=3&&j<=12) j++;//找到最长的顺子 if(j-i<2) continue;//长度必须满足条件 for(int k=j;k-i>=2;k--)//枚举长度,注意从长到短枚举,长的更优 { for(int l=i;l<k;l++) num[l]-=3;//更新牌数,模拟出牌过程 cnt++;dfs();cnt--; for(int l=i;l<k;l++) num[l]+=3;//回溯,恢复之前状态,注意cnt的恢复 } } for(int i=1;i<=10;i++)//模拟双顺子 { int j=i; while(num[j]>=2&&j<=12) j++; if(j-i<3) continue; for(int k=j;k-i>=3;k--) { for(int l=i;l<k;l++) num[l]-=2; cnt++;dfs();cnt--; for(int l=i;l<k;l++) num[l]+=2; } } for(int i=1;i<=8;i++)//模拟单顺子 { int j=i; while(num[j]>=1&&j<=12) j++; if(j-i<5) continue; for(int k=j;k-i>=5;k--) { for(int l=i;l<k;l++) num[l]-=1; cnt++;dfs();cnt--; for(int l=i;l<k;l++) num[l]+=1; } } } void init(){mem(num,0);ans=0x7fffffff;cnt=0;} int Anonymity() { //freopen("landlords.in","r",stdin); //freopen("landlords.out","w",stdout); t=read();n=read(); while(t--) { int tmp,tmp2;init();//初始化 for(int i=1;i<=n;i++) tmp=read(),tmp2=read(),num[st(tmp)]++; dfs();printf("%d\n",ans); } return 0; } int sakural=Anonymity();//两个id ^.^ int main(){;}
相关文章推荐
- [NOIP2015]斗地主 搜索+贪心
- 【NOIP2015】斗地主
- noip2015 斗地主
- 洛谷P2668 斗地主 [NOIP2015]
- noip2015 day1 斗地主
- 4325. 【NOIP2015提高组Day1】斗地主
- bzoj 4325: NOIP2015 斗地主 dfs
- BZOJ_4325_NOIP2015 斗地主_DFS
- 【UOJ #147】【NOIP 2015】斗地主
- [BZOJ 4325][NOIP 2015] 斗地主
- NOIP 2015 提高组 Day1 斗地主
- 【bzoj4325】NOIP2015 斗地主(&“加强”版) 搜索
- 【NOIP2015】Day1T3 斗地主
- 【BZOJ4325】【NOIP2015】斗地主 搜索
- [noip2015]斗地主(dfs+贪心)
- [noip2015]斗地主 题解
- [BZOJ4325]NOIP2015 斗地主
- NOIP2015复赛提高组day1(A:神奇的幻方 B:信息传递 C:斗地主)
- 【NOIP 2015】斗地主
- NOIP 2015 斗地主