CF919F

题意:

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值