• 【洛谷5370】[PKUSC2018] 主斗地(暴搜)


    点此看题面

    • 给出斗地主的若干种出牌牌型(详见原题面)。
    • 现在有两个队友(A,B)。每轮(A)出一种牌型,(B)出一种同牌型的严格更大的牌,若二人能一起把牌打完则获胜。
    • 从一副牌中分给每人(17)张,给定(B)的手牌,问(A)有多少种可能的手牌使得二人能获胜。(手牌中已除去(3)

    暴搜所有情况+暴搜验证

    由于已经除去了(3),所以(A)可能的手牌种类是很少的,我们可以直接暴搜出所有情况。

    然后考虑验证,我们发现对子、三张牌、顺子、连对、三顺都可以直接拆成单牌,飞机可以直接拆成三带一和三带二。

    因此我们实际上只用搜出三带一、三带二、四带二的情况,剩余的单牌排个序之后直接比较。

    更进一步,其实我们只用搜出三与三配对、四与四配对各自的数目,验证时枚举三三中有多少个带一,则必然贪心地带走(A)中最大的那些,带走(B)中最小的那些。

    而搜三与三配对和四与四配对的时候有个优化,就是四与四配对肯定选择最近的四,三与三配对可能选择最近的三,但也有可能选择之后任意一个四中的三个。

    具体实现详见代码,一开始智障写错不少细节。

    代码:(O()能过())

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    using namespace std;
    int s[20],a[20],b[20],c[20];string st;
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	char oc,FI[FS],*FA=FI,*FB=FI;
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }using namespace FastIO;
    int qa[20],qb[20],ta[20],tb[20];I bool Check(CI o)//贪心检验
    {
    	RI i,j,k,x,s=0;for(i=1;i<=14;++i) s+=a[i];RI w=(17-s-4*o)/3;if(w+2*o>s) return 0;
    	RI t;for(x=0;x<=w;++x)//枚举三带一的个数
    	{
    		for(i=1;i<=14;++i) ta[i]=a[i],tb[i]=b[i];//复制一份
    		for(i=14,j=x+2*o,k=w-x;i>=1;--i) {W(ta[i]>=2&&k) ta[i]-=2,--k;W(ta[i]&&j) --ta[i],--j;}if(j||k) continue;//贪心选大的
    		for(i=1,j=x+2*o,k=w-x;i<=14;++i) {W(tb[i]>=2&&k) tb[i]-=2,--k;W(tb[i]&&j) --tb[i],--j;}if(j||k) continue;//贪心选小的
    		for(t=0,i=1;i<=14;++i) for(j=1;j<=ta[i];++j) qa[++t]=i;
    		for(t=0,i=1;i<=14;++i) for(j=1;j<=tb[i];++j) qb[++t]=i;
    		for(i=1;i<=t;++i) if(qa[i]>=qb[i]) break;if(i>t) return 1;//有序,直接比较
    	}return 0;
    }
    I bool C3(RI x,RI y,RI z,CI o)//三与三配对
    {
    	W(x<=14&&a[x]<3) ++x;W(y<=14&&(y<=x||b[y]^3)) ++y;W(z<=14&&(z<=x||b[z]^4)) ++z;
    	if(y>14&&z>14) return Check(o);if(C3(x+1,y,z,o)||(z<=14&&C3(x,y,z+1,o))) return 1;//不配对,或不与这个四配对
    	if(y<=14) {a[x]-=3,b[y]-=3;RI t=C3(x+1,y+1,z,o);a[x]+=3,b[y]+=3;if(t) return 1;}//与最近的三配对
    	if(z<=14) {a[x]-=3,b[z]-=3;RI t=C3(x+1,y,z+1,o);a[x]+=3,b[z]+=3;if(t) return 1;}return 0;//与某个四配对
    }
    I bool C4(RI x,RI y,CI o)//四与四配对
    {
    	W(x<=14&&a[x]^4) ++x;W(y<=14&&(y<=x||b[y]^4)) ++y;if(y>14) return C3(1,1,1,o);//不配对
    	if(C4(x+1,y,o)) return 1;a[x]-=4,b[y]-=4;RI t=C4(x+1,y+1,o+1);a[x]+=4,b[y]+=4;return t;//与最近的四配对
    }
    int ans;I void dfs(CI x,CI t)//暴搜所有情况
    {
    	if(t==17) return (void)(ans+=C4(1,1,0));if(x>14) return;//牌满之后就去验证
    	for(RI i=0;i<=min(c[x]-b[x],17-t);++i) a[x]=i,dfs(x+1,t+i);a[x]=0;//暴搜x选几张
    }
    int main()
    {
    	#define V(x) (x=='T'?7:(x=='J'?8:(x=='Q'?9:(x=='K'?10:
    		(x=='A'?11:(x=='2'?12:(x=='w'?13:(x=='W'?14:x-51))))))))//按大小转化为数字
    	RI i;for(cin>>st,i=1;i<=14;++i) a[i]=b[i]=0,c[i]=i<=12?4:1;//c记录每种牌的数量
    	for(i=1;i<=17;++i) ++b[s[i]=V(st[i-1])];return dfs(1,0),printf("%d
    ",ans),0;//记录B每种牌数,暴搜
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    如何垂直居中一个浮动元素?
    微信小程序vant-search获取不到输入的搜索内容踩坑
    关于微信小程序无法使用css3过度属性transition以及微信小程序如何利用api进行画面过度的展示
    Spring Shedule Task之注解实现 (两次启动Schedule Task 的解决方案)
    学习站点记录
    Spring 通过配置文件注入 properties文件
    Liunx下安装jdk
    Mybatis使用generator自动生成映射配置文件信息
    Tomcat容器虚拟路径设置
    Spring init-method和destroy-method 的使用
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5370.html
Copyright © 2020-2023  润新知