“蔚来杯“2022牛客暑期多校训练营8 D题: Poker Game: Decision

D题: Poker Game: Decision

原题链接:https://ac.nowcoder.com/acm/contest/33193/D

题目大意

在德州扑克的大小比较规则情况下,发牌顺序与传统德扑不同。

初始发给Alice和Bob各两种手牌且双方均知道对方的手牌。牌桌上有 6 6 6 张牌。双方轮流选取一张牌,直到各有 5 5 5 张牌。Alice先手,牌组更大者获胜,求双方均采取最优策略的情况下,谁获胜。

德州扑克比较规则可参考:百度百科:德克萨斯扑克

题解

暴搜模拟然后基础博弈论转移即可,最麻烦的部分是大小比较。

关于牌型

不妨将同花和顺子(注意特判 A 2345 A2345 A2345 )作为标记保存下来。
对于牌的点数可以保存入一个计数数组进行排序,方便知道第 i i i 多的牌有多少张。

同牌型大小比较

1.皇家同花顺

只能是平局。

2.同花/高牌

排序后从大到小依次比较即可。

3.顺子/同花顺

首先特判最小的顺子 A 2345 A2345 A2345 ,然后各自取最大的一张进行比较即可。

4.四张/葫芦/三张/两对/一对

不难发现这些牌型的比较都呈现 4   1 / 3   2 / 3   1   1 / 2   2   1 / 2   1   1   1 4\ 1/3\ 2/3\ 1\ 1/2\ 2\ 1/2\ 1\ 1\ 1 4 1/3 2/3 1 1/2 2 1/2 1 1 1 的先比较张数多的牌的规律。
不妨重新利用计数数组,以张数为第一关键字,点数为第二关键字进行排序,然后从大到小依次比较即可。

两个优化

  1. 在搜索到必胜态时可以直接返回剪枝。
  2. 考虑操作序列有 6 ! 6! 6! 种,但是两个人得到牌的情况只有 C 6 3 C_6^3 C63 种,可以状压记忆化Alice选了哪些牌。

参考代码

#include<bits/stdc++.h>
#define P pair<int,int>
using namespace std;

template<class T>inline void read(T&x){
	char c,last=' ';
	while(!isdigit(c=getchar()))last=c;
	x=c^48;
	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+(c^48);
	if(last=='-')x=-x;
}

map<char,int>mp;
void init()//字符转点数/花色
{
	mp['A']=14;
	mp['K']=13;
	mp['Q']=12;
	mp['J']=11;
	mp['T']=10;
	mp['9']=9;
	mp['8']=8;
	mp['7']=7;
	mp['6']=6;
	mp['5']=5;
	mp['4']=4;
	mp['3']=3;
	mp['2']=2;
	mp['1']=1;
	
	mp['S']=1;
	mp['H']=2;
	mp['C']=3;
	mp['D']=4;
}

struct card{
	int num,suit;
};

bool cmp(const card&a,const card&b){return a.num>b.num;}//根据点数从大到小

struct cards{
	card c[5];
	void init(){
		sort(c,c+5,cmp);
	}
};

int type(cards a){//获得牌型
	a.init();
	vector<int>cnt(15);//计数数组
	int is_flush=1,is_straight=0;//标记:是否为同花/顺子
	for(int i=0;i<5;++i){
		if(i<4)is_flush&=a.c[i].suit==a.c[i+1].suit;
		++cnt[a.c[i].num];
	}
	sort(cnt.begin(),cnt.end(),greater<int>());
	if(a.c[0].num==14&&a.c[1].num==5&&a.c[2].num==4&&a.c[3].num==3&&a.c[4].num==2)is_straight=1;//特判A2345
	if(a.c[0].num==a.c[1].num+1&&a.c[1].num==a.c[2].num+1&&a.c[2].num==a.c[3].num+1&&a.c[3].num==a.c[4].num+1)is_straight=1;
	if(is_flush&&is_straight&&a.c[4].num==14)return 1;//皇家同花顺
	if(is_flush&&is_straight)return 2;//同花顺
	if(cnt[0]==4)return 3;//四张
	if(cnt[0]==3&&cnt[1]==2)return 4;//葫芦
	if(is_flush)return 5;//同花
	if(is_straight)return 6;//顺子
	if(cnt[0]==3)return 7;//三张
	if(cnt[0]==2&&cnt[1]==2)return 8;//两对
	if(cnt[0]==2)return 9;//一对
	return 10;//高牌
}

int cmp_royal(cards a,cards b){//皇家同花顺
	return 3;
}

int cmp_regular(cards a,cards b){//同花/高牌
	a.init(),b.init();
	for(int i=0;i<5;++i){
		if(a.c[i].num>b.c[i].num)return 1;
		if(b.c[i].num>a.c[i].num)return 2;
	}
	return 3;
}

int cmp_straight(cards a,cards b){//同花顺/顺子
	a.init(),b.init();
	int x=a.c[0].num==14&&a.c[4].num==2;//特判A2345是最小的顺子
	int y=b.c[0].num==14&&a.c[4].num==2;
	if(x&&y)return 3;
	if(x)return 2;
	if(y)return 1;
	x=a.c[0].num;
	y=b.c[0].num;
	if(x>y)return 1;
	if(y<x)return 2;
	return 3;
}

int cmp_groups(cards a,cards b){//葫芦/四张/三张/两对/一对
	vector<P>suma(15),sumb(15);//计数数组
	for(int i=2;i<=14;++i)suma[i].second=sumb[i].second=i;//以点数为第二关键字
	for(int i=0;i<5;++i)++suma[a.c[i].num].first,++sumb[b.c[i].num].first;//以牌数为第一关键字
	sort(suma.begin(),suma.end());//std::pair在比较时默认先比第一关键字,再比第二关键字
	sort(sumb.begin(),sumb.end());
	for(int i=14,p1=0,p2=0;i>=2;--i){//从大到小重新整牌
		while(suma[i].first)a.c[p1++].num=suma[i].second,--suma[i].first;
		while(sumb[i].first)b.c[p2++].num=sumb[i].second,--sumb[i].first;
	}
	for(int i=0;i<5;++i){//比较
		if(a.c[i].num>b.c[i].num)return 1;
		if(b.c[i].num>a.c[i].num)return 2;
	}
	return 3;
}

int cmp(cards a,cards b){//手牌比较
	int ta=type(a),tb=type(b);
//	cerr<<ta<<' '<<tb<<'\n';
	if(ta<tb)return 1;
	if(tb<ta)return 2;
	if(ta==1)return cmp_royal(a,b);
	if(ta==5||ta==10)return cmp_regular(a,b);
	if(ta==2||ta==6)return cmp_straight(a,b);
	if(ta==3||ta==4||ta==7||ta==8||ta==9)return cmp_groups(a,b);
	return 3;//这句话不会被调用,只适用于通过编译防止报错
}

cards A,B;
card c[6];
int b[6];
int ans[100];//记忆化

void debug(){//调试:输出手牌
	for(int i=0;i<5;++i){
		cerr<<A.c[i].num<<' '<<A.c[i].suit<<"  ";
	}
	puts("");
	for(int i=0;i<5;++i){
		cerr<<B.c[i].num<<' '<<B.c[i].suit<<"  ";
	}
	puts("");
	puts("");
}

int dfs(int d,int p1,int p2,int bit){//bit为状压的Alice选取的牌
	if(d>6){
//		debug();
		if(ans[bit])return ans[bit];//已经算过了
		return ans[bit]=cmp(A,B);
	}
	if(d&1){
		int f=0;
		for(int i=0;i<6;++i){
			if(b[i])continue;
			A.c[p1]=c[i];
			b[i]=1;
			int res=dfs(d+1,p1+1,p2,bit|1<<i);
			b[i]=0;
			if(res==1)return 1;//剪枝,注意放在b[i]=0回溯之后
			f|=res==3;
		}
		return f?3:2;//能平则平
	}
	else{
		int f=0;
		for(int i=0;i<6;++i){
			if(b[i])continue;
			B.c[p2]=c[i];
			b[i]=1;
			int res=dfs(d+1,p1,p2+1,bit);
			b[i]=0;
			if(res==2)return 2;//剪枝
			f|=res==3;
		}
		return f?3:1;
	}
}

int main()
{
	init();
	int T;read(T);
	while(T--){
		memset(ans,0,sizeof(ans));//清空记忆化数组
		string s;
		for(int i=0;i<2;++i){
			cin>>s;
			A.c[i].num=mp[s[0]];
			A.c[i].suit=mp[s[1]];
		}
		for(int i=0;i<2;++i){
			cin>>s;
			B.c[i].num=mp[s[0]];
			B.c[i].suit=mp[s[1]];
		}
		for(int i=0;i<6;++i){
			cin>>s;
			c[i].num=mp[s[0]];
			c[i].suit=mp[s[1]];
		}
		int res=dfs(1,2,2,0);
		if(res==1)puts("Alice");
		if(res==2)puts("Bob");
		if(res==3)puts("Draw");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值