cunzai_zsy0531

关注我

P7147 [THUPC2021初赛]麻将模拟器 题解

做完 pkusc2022 day2t3 雀圣之后跑来做这题。

首先我们考虑雀圣那题。那个题是说给你一副 \(13\) 张的手牌,让你算向听数。设 \(dp[i][j][0/1][p1][p2][up][dn]=0/1\) 表示考虑到 \(i\) 花色第 \(j\) 张牌,是否已经有雀头,上张牌剩下 \(p1\) 张,这张牌剩下 \(p2\) 张,已经加了 \(up\) 张,已经扔了 \(dn\) 张的状态是否可以达到。枚举当前这张牌加几张、删几张,是否作为雀头,能作为刻子就一定作为刻子,和前两张牌组成顺子,然后转移一下。每一种花色转移完之后清掉除 \(p1=p2=0\) 之外的所有状态即可。

有了这个 dp 这题剩下的就不难了。特殊牌的话就先处理一下,PASS 就多跳一个,DOUBLEREVERSE 就定义一个 \(delta=1/-1\),每次加 \(delta\) 表示顺/逆时针即可。进入打牌阶段之后,对每个玩家记一个当前和牌距离。打牌就是每次枚举打哪一张,dp算一下取一个和牌距离最短且最大的,打出去之后再对另外的玩家判一下碰和吃即可。虽然复杂度比较高,但是看起来可以过。

code(8.3k):

点击查看代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#define memset __builtin_memset
#define mp std::make_pair
#define fi first
#define se second
typedef std::pair<int,int> pii;
std::map<char,int> ms;
inline void chkmin(int &a,const int &b){if(b<a)a=b;}
inline char cc(int x){return (char)(x+'A');}
inline char cn(int x){return (char)(x+'0');}
char nam[8]={'0','E','S','W','N','B','F','Z'},nam1[3]={'M','P','S'};
int limt[5]={9,9,9,7,3};
std::string nam2[4]={"","PASS","REVERSE","DOUBLE"};
inline void init(){
	ms['E']=1,ms['S']=2,ms['W']=3,ms['N']=4,ms['B']=5,ms['F']=6,ms['Z']=7;
}
inline std::string f(pii x){
	std::string res="";
	if(x.fi<=2) res+=cn(x.se),res+=nam1[x.fi];
	else if(x.fi==3) res+=nam[x.se];
	else res=nam2[x.se];
	return res;
}
int a[4][5][10],to[4];
inline void print_in(int o,const char *in){printf("%c IN %s\n",cc(o),in);}
inline void print_out(int o,const char *out,int oo){
	if(out[0]=='P') printf("%c OUT %s %c\n",cc(o),out,cc(oo));
	else printf("%c OUT %s\n",cc(o),out);
}
inline void print_pong(int o,const char *pong){printf("%c PONG %s %s %s\n",cc(o),pong,pong,pong);}
inline void print_chow(int o,const char *chow1,const char *chow2,const char *chow3){printf("%c CHOW %s %s %s\n",cc(o),chow1,chow2,chow3);}
inline void print_selfdrawn(int o){printf("%c SELFDRAWN\n",cc(o));}
inline void print_ron(int o){printf("%c RON\n",cc(o));}
inline void print_win(int o){printf("%c WIN\n",cc(o));}
inline void rd(int o,const char *in){
	if(in[0]=='P') a[o][4][1]++;
	else if(in[0]=='R') a[o][4][2]++;
	else if(in[0]=='D') a[o][4][3]++;
	else if(in[0]>='1'&&in[0]<='9'){
		int tmp;
		if(in[1]=='M') tmp=0;else if(in[1]=='P') tmp=1;else tmp=2;
		a[o][tmp][in[0]-'0']++;
	}
	else a[o][3][ms[in[0]]]++;
}
inline int findans(int a[5][10],bool flag=0){
	static bool dp[6][10][2][3][3][14][13];
	memset(dp,0,sizeof dp);
	dp[0][0][0][0][0][0][0]=1;
	for(int o=0;o<=4;++o){
		for(int i=1;i<=limt[o];++i){
			for(int qt=0;qt<=1;++qt)
			for(int p1=0;p1<=2;++p1)
			for(int p2=p1;p2<=2;++p2)
			for(int up=0;up<=13;++up)
			for(int dn=0;dn<=12;++dn){
				if(!dp[o][i-1][qt][p1][p2][up][dn]) continue;
				//不加不减
				int now=a[o][i];
				//雀头 
				if(o<=3&&!qt&&now>=2){
					now-=2;
					if(!p1) dp[o][i][1][p2][now][up][dn]=1;
					if(o<=2&&p1==1&&p2>=1&&now>=1) dp[o][i][1][p2-1][now-1][up][dn]=1;
					if(o<=2&&p1==2&&p2>=2&&now>=2) dp[o][i][1][p2-2][now-2][up][dn]=1;
					now+=2;
				}
				//是否有刻子 
				if(o<=3&&now>=3){
					now-=3;
					if(!p1) dp[o][i][qt][p2][now][up][dn]=1;
					if(o<=2&&p1==1&&p2>=1&&now>=1) dp[o][i][qt][p2-1][now-1][up][dn]=1;	
				}
				else{
					if(!p1) dp[o][i][qt][p2][now][up][dn]=1;
					if(o<=2&&p1==1&&p2>=1&&now>=1) dp[o][i][qt][p2-1][now-1][up][dn]=1;
					if(o<=2&&p1==2&&p2>=2&&now>=2) dp[o][i][qt][p2-2][now-2][up][dn]=1;
				}
				for(int j=1;j<=4-a[o][i]&&up+j<=13;++j){//加 
					now=a[o][i]+j;
					if(o<=3&&!qt&&now>=2){
						now-=2;
						if(!p1) dp[o][i][1][p2][now][up+j][dn]=1;
						if(o<=2&&p1==1&&p2>=1&&now>=1) dp[o][i][1][p2-1][now-1][up+j][dn]=1;
						if(o<=2&&p1==2&&p2>=2&&now>=2) dp[o][i][1][p2-2][now-2][up+j][dn]=1;
						now+=2;
					}
					if(o<=3&&now>=3){
						now-=3;
						if(!p1) dp[o][i][qt][p2][now][up+j][dn]=1;
						if(o<=2&&p1==1&&p2>=1&&now>=1) dp[o][i][qt][p2-1][now-1][up+j][dn]=1;	
					}
					else{
						if(!p1) dp[o][i][qt][p2][now][up+j][dn]=1;
						if(o<=2&&p1==1&&p2>=1&&now>=1) dp[o][i][qt][p2-1][now-1][up+j][dn]=1;
						if(o<=2&&p1==2&&p2>=2&&now>=2) dp[o][i][qt][p2-2][now-2][up+j][dn]=1;
					}
				}
				for(int j=1;j<=a[o][i]&&dn+j<=12;++j){//减 
					now=a[o][i]-j;
					if(o<=3&&!qt&&now>=2){
						now-=2;
						if(!p1) dp[o][i][1][p2][now][up][dn+j]=1;
						if(o<=2&&p1==1&&p2>=1&&now>=1) dp[o][i][1][p2-1][now-1][up][dn+j]=1;
						if(o<=2&&p1==2&&p2>=2&&now>=2) dp[o][i][1][p2-2][now-2][up][dn+j]=1;
						now+=2;
					}
					if(o<=3&&now>=3){
						now-=3;
						if(!p1) dp[o][i][qt][p2][now][up][dn+j]=1;
						if(o<=2&&p1==1&&p2>=1&&now>=1) dp[o][i][qt][p2-1][now-1][up][dn+j]=1;	
					}
					else{
						if(!p1) dp[o][i][qt][p2][now][up][dn+j]=1;
						if(o<=2&&p1==1&&p2>=1&&now>=1) dp[o][i][qt][p2-1][now-1][up][dn+j]=1;
						if(o<=2&&p1==2&&p2>=2&&now>=2) dp[o][i][qt][p2-2][now-2][up][dn+j]=1;
					}
				} 
			}
		}
		for(int qt=0;qt<=1;++qt)
		for(int up=0;up<=13;++up)
		for(int dn=0;dn<=12;++dn)
			dp[o+1][0][qt][0][0][up][dn]=dp[o][limt[o]][qt][0][0][up][dn];
	}
	for(int ans=0;ans<=12;++ans)
		if(dp[5][0][1][0][0][ans+flag][ans]) return ans+flag;
}
inline pii findout(int o){
	for(int i=1;i<=3;++i)
		if(a[o][4][i]) return --a[o][4][i],mp(4,i);
	int res=13;pii ans;
	for(int oo=3;oo>=0;--oo)
		for(int i=limt[oo];i;--i){
			if(!a[o][oo][i]) continue;
			a[o][oo][i]--;int tmp;
			if((tmp=findans(a[o],1))<res) res=tmp,ans=mp(oo,i);
			a[o][oo][i]++;
		}
	return --a[o][ans.fi][ans.se],to[o]=res,ans;
}
inline bool drawn(int a[5][10]){
	if(a[4][1]||a[4][2]||a[4][3]) return 0;
	int b[5][10];
	for(int oo=0;oo<=3;++oo)
	for(int ii=1;ii<=limt[oo];++ii){
		for(int ooo=0;ooo<=3;++ooo)for(int iii=1;iii<=limt[ooo];++iii)b[ooo][iii]=a[ooo][iii];
		if(b[oo][ii]<2) continue;
		b[oo][ii]-=2;bool ok=1;
		for(int o=0;o<=3;++o){
			for(int i=1;i<=limt[o];++i){
				if(b[o][i]>=3) b[o][i]-=3;
				if(o==3&&b[o][i]){ok=0;break;}
				while(b[o][i]){
					if(i>7||!b[o][i+1]||!b[o][i+2]){ok=0;break;}
					--b[o][i],--b[o][i+1],--b[o][i+2];
				}
				if(!ok) break;
			}
			if(!ok) break;
		}
		if(ok) return 1;
	}
	return 0;
} 
inline bool ron(int o,pii x){
	a[o][x.fi][x.se]++;
	bool ans=drawn(a[o]);
	a[o][x.fi][x.se]--;
	return ans;
}
inline bool pong(int o,pii x){
	if(a[o][x.fi][x.se]<2) return 0;
	a[o][x.fi][x.se]-=2;
	int now=findans(a[o]);
	if(now<to[o]) return to[o]=now,1;
	return a[o][x.fi][x.se]+=2,0;
}
inline int chow(int o,pii x){
	if(x.se<=7&&a[o][x.fi][x.se+1]&&a[o][x.fi][x.se+2]){
		a[o][x.fi][x.se+1]--,a[o][x.fi][x.se+2]--;
		int now=findans(a[o]);
		if(now<to[o]) return to[o]=now,1;
		a[o][x.fi][x.se+1]++,a[o][x.fi][x.se+2]++;
	}
	if(x.se<=8&&x.se>=2&&a[o][x.fi][x.se-1]&&a[o][x.fi][x.se+1]){
		a[o][x.fi][x.se-1]--,a[o][x.fi][x.se+1]--;
		int now=findans(a[o]);
		if(now<to[o]) return to[o]=now,2;
		a[o][x.fi][x.se-1]++,a[o][x.fi][x.se+1]++;
	}
	if(x.se>=3&&a[o][x.fi][x.se-2]&&a[o][x.fi][x.se-1]){
		a[o][x.fi][x.se-2]--,a[o][x.fi][x.se-1]--;
		int now=findans(a[o]);
		if(now<to[o]) return to[o]=now,3;
		a[o][x.fi][x.se-2]++,a[o][x.fi][x.se-1]++;
	}
	return 0;
}
int main(){
//	freopen("a.in","r",stdin);freopen("a.out","w",stdout);
//	return system("fc P7147_2.out data.out"),0;
#ifdef LOCAL
	freopen("29.in","r",stdin);
//	freopen("a.out","w",stdout);
#endif
	init();
	int o=0,delta=1,Cnt=52;
	while(Cnt--){
		char in[10];scanf("%s",in);
		print_in(o,in);rd(o,in);
		o=(o+delta+4)%4;
	}
	for(int pp=0;pp<=3;++pp){
		int res=13;
		for(int oo=0;oo<=3;++oo)
			for(int i=1;i<=limt[oo];++i)
				++a[pp][oo][i],chkmin(res,findans(a[pp])+1),--a[pp][oo][i];
		to[pp]=res;
	}
	Cnt=96;bool flag=0;int win=-1;
	while(Cnt--){
		int ff=1;char in[10];
		if(!flag){
			scanf("%s",in);
			print_in(o,in);rd(o,in);	
		}
		else ++Cnt,flag=0;
		if(drawn(a[o])){print_selfdrawn(o),win=o;break;}
		pii res=findout(o);
		print_out(o,f(res).c_str(),(o+delta*ff+4)%4);
		if(res.fi==4){
			if(res.se==2) delta*=-1;
			else if(res.se==3) ff=0;
			else o=(o+delta*ff+4)%4;
			o=(o+delta*ff+4)%4;
			continue;
		}
		if(res.fi<=3){
			for(int nn=(o+delta+4)%4;nn!=o;nn=(nn+delta+4)%4)
				if(ron(nn,res)){print_ron(nn),win=nn;break;}
			if(win!=-1) break;
			bool jump=0;
			for(int nn=0;nn<=3;++nn)
				if(nn!=o&&pong(nn,res)){print_pong(nn,f(res).c_str()),o=nn,flag=1,jump=1;break;}
			if(jump) continue;
		}
		if(res.fi<=2){
			int tmp;
			if(tmp=chow((o+delta+4)%4,res)){
				o=(o+delta+4)%4,flag=1;
				if(tmp==1) print_chow(o,f(mp(res.fi,res.se)).c_str(),f(mp(res.fi,res.se+1)).c_str(),f(mp(res.fi,res.se+2)).c_str());
				else if(tmp==2) print_chow(o,f(mp(res.fi,res.se-1)).c_str(),f(mp(res.fi,res.se)).c_str(),f(mp(res.fi,res.se+1)).c_str());
				else print_chow(o,f(mp(res.fi,res.se-2)).c_str(),f(mp(res.fi,res.se-1)).c_str(),f(mp(res.fi,res.se)).c_str());
				continue;
			}
		}
		o=(o+delta*ff+4)%4;
	}
	if(win==-1) puts("DRAW");
	else print_win(win);
	return 0;
}
posted @ 2022-05-27 20:43  cunzai_zsy0531  阅读(202)  评论(0编辑  收藏  举报