Logo Iceturky 的博客

博客

AT_icpc2012autumn_b Texas hold'em题解

...
Iceturky
2025-12-01 12:54:32
星屑落ちて 華は散っても キラめく舞台に 生まれて変わる

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-07-07 10:38:56

Link

怎么还有德扑的,不会打。

你是职业选手吗?我觉得我是。


我的大体思路是多用几个结构体。卡牌有结构体,准备出的5张牌也有结构体,对于后者比较麻烦,我在里面放了一个opt来表示其属于哪种等级,还放了若干函数来判断是哪种牌型。

主函数则直接枚举剩下的两张的取值,然后7选5排下序,两边各出最强牌。

排序的cmp函数写的比较长(

另外实现了一个按字典序比较两个vector的函数,感觉会比较方便。

写了6.5K,感觉还行。长但是好写。

code

const int N=15;

bool t[1000];

struct card{\/\/牌
	int c,w;
	card(){c=w=0;}
	bool operator<(card ano)const{
		return w>ano.w;
	}
	void mkcard(char *ss){\/\/制作卡牌
		if(ss[1]=='C')
			c=1;
		if(ss[1]=='D')
			c=2;
		if(ss[1]=='H')
			c=3;
		if(ss[1]=='S')
			c=4;
		if(ss[2]=='T')
			w=10;
		else if(ss[2]=='J')
			w=11;
		else if(ss[2]=='Q')
			w=12;
		else if(ss[2]=='K')
			w=13;
		else if(ss[2]=='A')
			w=14;
		else
			w=ss[2]-'0';
		t[c*15+w]=1;
	}
}my[N],ur[N];


struct handin{\/\/准备要出的5张牌,姑且称为手牌
	int opt;\/\/牌型
	card p[7];\/\/牌
	bool samecol(){\/\/是否同花
		bool tag=1;
		for(int i=2;i<=5;i++)
			tag&=p[i].c==p[1].c;
		return tag;
	}
	int continuous(){\/\/是否是顺子(注意特判皇家顺)
		if(p[5].w==2&&p[4].w==3&&p[3].w==4&&p[2].w==5&&p[1].w==14)
			return 1;
		bool tag=1;
		for(int i=2;i<=5;i++)
			tag&=p[i].w==p[i-1].w-1;
		return (int)tag+(tag&&(p[1].w==14));
	}
	bool four(){\/\/是否有炸弹
		map<int,int>mp;
		for(int i=1;i<=5;i++)
			mp[p[i].w]++;
		for(auto i:mp){
			if(i.se==4)
				return 1;
		}return 0;
	}
	bool three(){\/\/是否有三张
		map<int,int>mp;
		for(int i=1;i<=5;i++)
			mp[p[i].w]++;
		for(auto i:mp){
			if(i.se==3)
				return 1;
		}return 0;
	}
	int two(){\/\/有几对
		int num=0;
		map<int,int>mp;
		for(int i=1;i<=5;i++)
			mp[p[i].w]++;
		for(auto i:mp){
			num+=i.se==2;
		}return num;
	}
	void load_handin(){\/\/加载牌型
		sort(p+1,p+6);
		if(samecol()&&continuous())\/\/同花顺&皇家同花顺 
			opt=8+continuous();
		else if(four())\/\/炸弹 
			opt=8;
		else if(three()&&two())\/\/三带二 
			opt=7;
		else if(samecol())\/\/同花 
			opt=6;
		else if(continuous())\/\/顺子 
			opt=5;
		else if(three())\/\/三 
			opt=4;
		else if(two()==2)\/\/两对 
			opt=3;
		else if(two()==1)\/\/一对 
			opt=2;
		else\/\/散牌
			opt=1;
	}
};

bool compare_less(handin a,handin b){\/\/比较两个vector
	for(int i=1;i<=5;i++)
		if(a.p[i].w!=b.p[i].w)
			return a.p[i].w>b.p[i].w;
	return 0;
}

bool cmp(handin a,handin b){\/\/比较两组牌谁更大,值得注意的是两组牌都是已经按照数字大小排过序的了(在load_handin函数里)
	if(a.opt!=b.opt)\/\/不同牌型
		return a.opt>b.opt;
	else{
		handin x,y;
		map<int,int> mpa,mpb;
		for(int i=1;i<=5;i++)
			mpa[a.p[i].w]++,mpb[b.p[i].w]++;
		if(a.opt==10)\/\/皇家同花顺 
			return 0;
		else if(a.opt==9){\/\/同花顺
			if(a.p[1].w==14)\/\/因为一定不是皇家同花顺,那么这里的14就是1,只要有出现就说明是最小顺子
				return 0;
			else if(b.p[1].w==14)
				return 1;
			else
				return a.p[1].w>b.p[1].w;
		}else if(a.opt==8){\/\/炸弹 
			for(auto i:mpa){
				if(i.se==4)
					x.p[1].w=i.fi;\/\/优先比炸弹谁大
				else
					x.p[2].w=i.fi;
			}
			for(auto i:mpb){
				if(i.se==4)
					y.p[1].w=i.fi;
				else
					y.p[2].w=i.fi;
			}
			return compare_less(x,y);
		}else if(a.opt==7){\/\/三带二 
			for(auto i:mpa){
				if(i.se==3)
					x.p[1].w=i.fi;\/\/优先比3张谁大
				else
					x.p[2].w=i.fi;
			}
			for(auto i:mpb){
				if(i.se==3)
					y.p[1].w=i.fi;
				else
					y.p[2].w=i.fi;
			}
			return compare_less(x,y);
		}else if(a.opt==6){\/\/同花 直接比
			return compare_less(a,b); 
		}else if(a.opt==5){\/\/顺子 注意区分皇家顺与最小顺的14地位
			if(a.continuous()!=b.continuous())
				return a.continuous()>b.continuous();
			if(a.p[1].w==14)
				return 0;
			else if(b.p[1].w==14) 
				return 1;
			else
				return a.p[1].w>b.p[1].w;
		}else if(a.opt==4){\/\/三 
			int nw=4;
			for(auto i:mpa){
				if(i.se==3)
					x.p[1].w=i.fi;\/\/先比3
				else
					x.p[--nw].w=i.fi;\/\/其次比最大的单牌,在遍历map的时候是先小后大,所以要反过来放
            }
			nw=4;
			for(auto i:mpb){
				if(i.se==3)
					y.p[1].w=i.fi;
				else
					y.p[--nw].w=i.fi;
			}
			return compare_less(x,y);
		}else if(a.opt==3){\/\/两对 
			int nw=3;
			for(auto i:mpa){
				if(i.se==2)\/\/先比大对子在比小对子最后单牌
					x.p[--nw].w=i.fi;
				else
					x.p[3].w=i.fi;
			}
			nw=3;
			for(auto i:mpb){
				if(i.se==2)
					y.p[--nw].w=i.fi;
				else
					y.p[3].w=i.fi;
			}
			return compare_less(x,y);
		}else if(a.opt==2){\/\/一对 
			int nw=5;
			for(auto i:mpa){
				if(i.se==2)
					x.p[1].w=i.fi;\/\/先比对子再比最大单,次大单,最小单
				else
					x.p[--nw].w=i.fi;
			}
			nw=5;
			for(auto i:mpb){
				if(i.se==2)
					y.p[1].w=i.fi;
				else
					y.p[--nw].w=i.fi;
			}
			return compare_less(x,y);
		}else
			return compare_less(a,b);\/\/单牌直接挨个比
	}
}

void readin(){
	char ys1[N],ys2[N],os1[N],os2[N];
	char tbll[4][N];
	scanf("%s",ys1+1);
	if(ys1[1]=='#')
		exit(0);
	scanf("%s%s%s",ys2+1,os1+1,os2+1);
	for(int i=1;i<=3;i++)
		scanf("%s",tbll[i]+1);
	my[1].mkcard(ys1);
	my[2].mkcard(ys2);
	ur[1].mkcard(os1);
	ur[2].mkcard(os2);
	my[3].mkcard(tbll[1]);
	my[4].mkcard(tbll[2]);
	my[5].mkcard(tbll[3]);
	ur[3].mkcard(tbll[1]);
	ur[4].mkcard(tbll[2]);
	ur[5].mkcard(tbll[3]);\/\/制作卡牌
}

handin tmp[1000];

signed main(){
	while(1){
		memset(t,0,sizeof(t));
		readin();
		int sum=0,win=0;
		for(int ic=1;ic<=4;ic++){
			for(int iw=2;iw<=14;iw++){\/\/枚举第一张牌
				if(t[ic*15+iw])
					continue;
				t[ic*15+iw]=1;
				my[6].c=ic,my[6].w=iw;
				ur[6].c=ic,ur[6].w=iw;
				for(int jc=1;jc<=4;jc++){
					for(int jw=2;jw<=14;jw++){\/\/第二张
						if(t[jc*15+jw])
							continue;
						my[7].c=jc,my[7].w=jw;
						ur[7].c=jc,ur[7].w=jw;
						handin mygo,urgo;\/\/两人准备要出的牌
						int all=(1<<7)-1;
						int tot=0;
						for(int k=1;k<=all;k++){
							if(__builtin_popcount(k)==5){
								int nw=0;
								++tot;
								for(int j=1;j<=7;j++)
									if(k&(1<<(j-1)))
										tmp[tot].p[++nw]=my[j];
								tmp[tot].load_handin();
							}
						}
						sort(tmp+1,tmp+1+tot,cmp);
						mygo=tmp[1];
						tot=0;
						for(int k=1;k<=all;k++){
							if(__builtin_popcount(k)==5){
								int nw=0;
								++tot;
								for(int j=1;j<=7;j++)
									if(k&(1<<(j-1)))
										tmp[tot].p[++nw]=ur[j];
								tmp[tot].load_handin();
							}
						}
						sort(tmp+1,tmp+1+tot,cmp);
						urgo=tmp[1];
						win+=cmp(mygo,urgo);\/\/直接比较即可
						sum++;
					}
				}
				t[ic*15+iw]=0;
			}
		}
\/\/		cout<<win<<" "<<sum<<endl;
		printf("%.10lf\n",1.0*win\/sum);
	}
	return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。