标签:ret span stream emc 操作 math cst scan 好用
题意:
Alice和Bob玩游戏,每人各有8张牌,牌的大小在0~4之间
每次操作,先手可以选择自己一张牌和对方一张牌求和后%5,将新的牌替代自己拿出的那张牌,以此类推,直到有一个人手中的牌全部是0,则这个人获胜
但选牌时不能选择已经为0的牌进行操作
现给定初始状态下两人的手牌以及先后手,求是否存在有人必胜
分析:
很显然是个博弈问题,对这种问题搜索是非常好用的。
我们只需考虑一下设计状态
很显然,一个人手牌的顺序对结果是没有任何影响的,所以状态数其实并不多
那么我们不妨把所有状态设成手牌大小单调不降的。
然后用排列组合计算一下,得一个人手牌总方案数为495(这个有很多算法,网上常见的算法比较简单(隔板法),但如果不熟悉隔板法(比如我),就使用了诡异的组合法:
(分类讨论:
①:假设8张手牌的值相等,那么只会有C(5,1)种方案
②:假设8张手牌种出现了两种值,那么首先有C(5,2)种方法,同时考虑每种值出现的次数,发现有7种组合(1+7,2+6,3+5,4+4,5+3,6+2,7+1),所以这里的贡献是7*C(5,2)
③:假设8张手牌出现了3种值,那么首先有C(5,3)种方法,那么假设将这三个值放在前三位,剩下5个位置可以递归成①,②和③来处理...
以此类推,最后将方案数累加,可以得出结果是495
(天知道为什么我要用这么复杂的方法))
那么,两个人的所有状态就是495^2,也是可以接受的
接下来,两个状态之间会有相互的转移关系(先手的操作会把一种状态转变成另一种状态),那么我们对所有状态重新编号(这里我使用hash+map来实现),然后枚举所有的转移方案
如果状态i可以转移至状态j,那么由J向I建一条边!(反向建边)
然后,我们枚举所有状态,一定有一些状态是还没开始就结束(即一定先手必胜或先手必败的),那这些状态就是初始状态,直接推进队列里然后bfs,处理出剩下状态的情况,这也是反向建边的目的
博弈搜索的原则:如果一个状态的后继状态中存在先手必败的状态,则这个状态为先手必胜,但如果所有后继状态都是先手必胜,那么这个状态就是先手必败的,但如果这个状态无法入队,则这个状态就是平局
这样就完事了,预处理出所有状态的胜负手,然后直接输出答案即可
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> #include <map> #define seed 13131 #define ull unsigned long long using namespace std; struct Edge { int next; int to; }edge[250005*64]; struct node { int a[9]; int b[9]; ull hh; int typ; }sit[250005]; int x[9]; int temp[9]; int temp2[9]; int tempsit[250005][9]; int head[250005]; int ta[9],tb[9]; int inr[250005]; int cnt=1; int tot=0; int cct=0; map <ull,int> M,nnum,used[250005]; queue <int> Q; void init() { memset(head,-1,sizeof(head)); cnt=1; } void add(int l,int r) { edge[cnt].next=head[l]; edge[cnt].to=r; head[l]=cnt++; } void dfs(int dep) { if(dep==9) { memcpy(temp,x,sizeof(x)); sort(temp+1,temp+dep); ull has=1; for(int i=1;i<=8;i++) { has=has*seed+temp[i]; } if(!M[has]) { M[has]=1; tot++; memcpy(tempsit[tot],temp,sizeof(temp)); } return; } for(int i=0;i<=4;i++) { x[dep]=i; dfs(dep+1); } } void judge() { for(int i=1;i<=tot*tot;i++) { bool flag=0; for(int j=1;j<=8;j++) { if(sit[i].a[j]!=0) { flag=1; break; } } if(!flag) { sit[i].typ=1; Q.push(i); continue; } flag=0; for(int j=1;j<=8;j++) { if(sit[i].b[j]!=0) { flag=1; break; } } if(!flag) { sit[i].typ=2; Q.push(i); continue; } } } void make_sit() { for(int i=1;i<=tot;i++) { for(int j=1;j<=tot;j++) { memcpy(sit[(i-1)*tot+j].b,tempsit[j],sizeof(sit[j].a)); memcpy(sit[(i-1)*tot+j].a,tempsit[i],sizeof(sit[i].a)); ull has=1; for(int k=1;k<=8;k++) { has=has*seed+sit[(i-1)*tot+j].a[k]; } for(int k=1;k<=8;k++) { has=has*seed+sit[(i-1)*tot+j].b[k]; } nnum[has]=(i-1)*tot+j; sit[(i-1)*tot+j].hh=has; } } } void add_edge() { for(int i=1;i<=tot*tot;i++) { for(int j=1;j<=8;j++) { if(sit[i].a[j]==0) { continue; } for(int k=1;k<=8;k++) { if(sit[i].b[k]==0) { continue; } int t=(sit[i].a[j]+sit[i].b[k])%5; memcpy(temp,sit[i].b,sizeof(temp)); memcpy(temp2,sit[i].a,sizeof(temp2)); temp2[j]=t; ull has=1; sort(temp+1,temp+9); sort(temp2+1,temp2+9); for(int p=1;p<=8;p++) { has=has*seed+temp[p]; } for(int p=1;p<=8;p++) { has=has*seed+temp2[p]; } if(used[i][has]) { continue; } used[i][has]=1; add(nnum[has],i); inr[i]++; } } } } void bfs() { while(!Q.empty()) { int u=Q.front(); Q.pop(); for(int i=head[u];i!=-1;i=edge[i].next) { int to=edge[i].to; if(!inr[to])continue; if(sit[u].typ==2) { sit[to].typ=1; inr[to]=0; Q.push(to); }else { inr[to]--; if(!inr[to]&&!sit[to].typ) { sit[to].typ=2; Q.push(to); } } } } } int main() { init(); dfs(1); make_sit(); judge(); add_edge(); bfs(); int T; scanf("%d",&T); while(T--) { int ty; scanf("%d",&ty); for(int i=1;i<=8;i++) { scanf("%d",&ta[i]); } for(int i=1;i<=8;i++) { scanf("%d",&tb[i]); } sort(ta+1,ta+9); sort(tb+1,tb+9); ull has=1; if(ty) { for(int i=1;i<=8;i++) { has=has*seed+tb[i]; } for(int i=1;i<=8;i++) { has=has*seed+ta[i]; } int t=nnum[has]; if(sit[t].typ==0) { printf("Deal\n"); continue; }else if(sit[t].typ==1) { printf("Bob\n"); continue; }else { printf("Alice\n"); continue; } }else { for(int i=1;i<=8;i++) { has=has*seed+ta[i]; } for(int i=1;i<=8;i++) { has=has*seed+tb[i]; } int t=nnum[has]; if(sit[t].typ==0) { printf("Deal\n"); continue; }else if(sit[t].typ==1) { printf("Alice\n"); continue; }else { printf("Bob\n"); continue; } } } return 0; }
标签:ret span stream emc 操作 math cst scan 好用
原文地址:https://www.cnblogs.com/zhangleo/p/9849085.html