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[*];
struct node
{
int a[];
int b[];
ull hh;
int typ;
}sit[];
int x[];
int temp[];
int temp2[];
int tempsit[][];
int head[];
int ta[],tb[];
int inr[];
int cnt=;
int tot=;
int cct=;
map <ull,int> M,nnum,used[];
queue <int> Q;
void init()
{
memset(head,-,sizeof(head));
cnt=;
}
void add(int l,int r)
{
edge[cnt].next=head[l];
edge[cnt].to=r;
head[l]=cnt++;
}
void dfs(int dep)
{
if(dep==)
{
memcpy(temp,x,sizeof(x));
sort(temp+,temp+dep);
ull has=;
for(int i=;i<=;i++)
{
has=has*seed+temp[i];
}
if(!M[has])
{
M[has]=;
tot++;
memcpy(tempsit[tot],temp,sizeof(temp));
}
return;
}
for(int i=;i<=;i++)
{
x[dep]=i;
dfs(dep+);
}
}
void judge()
{
for(int i=;i<=tot*tot;i++)
{
bool flag=;
for(int j=;j<=;j++)
{
if(sit[i].a[j]!=)
{
flag=;
break;
}
}
if(!flag)
{
sit[i].typ=;
Q.push(i);
continue;
}
flag=;
for(int j=;j<=;j++)
{
if(sit[i].b[j]!=)
{
flag=;
break;
}
}
if(!flag)
{
sit[i].typ=;
Q.push(i);
continue;
}
}
}
void make_sit()
{
for(int i=;i<=tot;i++)
{
for(int j=;j<=tot;j++)
{
memcpy(sit[(i-)*tot+j].b,tempsit[j],sizeof(sit[j].a));
memcpy(sit[(i-)*tot+j].a,tempsit[i],sizeof(sit[i].a));
ull has=;
for(int k=;k<=;k++)
{
has=has*seed+sit[(i-)*tot+j].a[k];
}
for(int k=;k<=;k++)
{
has=has*seed+sit[(i-)*tot+j].b[k];
}
nnum[has]=(i-)*tot+j;
sit[(i-)*tot+j].hh=has;
}
}
}
void add_edge()
{
for(int i=;i<=tot*tot;i++)
{
for(int j=;j<=;j++)
{
if(sit[i].a[j]==)
{
continue;
}
for(int k=;k<=;k++)
{
if(sit[i].b[k]==)
{
continue;
}
int t=(sit[i].a[j]+sit[i].b[k])%;
memcpy(temp,sit[i].b,sizeof(temp));
memcpy(temp2,sit[i].a,sizeof(temp2));
temp2[j]=t;
ull has=;
sort(temp+,temp+);
sort(temp2+,temp2+);
for(int p=;p<=;p++)
{
has=has*seed+temp[p];
}
for(int p=;p<=;p++)
{
has=has*seed+temp2[p];
}
if(used[i][has])
{
continue;
}
used[i][has]=;
add(nnum[has],i);
inr[i]++;
}
}
}
}
void bfs()
{
while(!Q.empty())
{
int u=Q.front();
Q.pop();
for(int i=head[u];i!=-;i=edge[i].next)
{
int to=edge[i].to;
if(!inr[to])continue;
if(sit[u].typ==)
{
sit[to].typ=;
inr[to]=;
Q.push(to);
}else
{
inr[to]--;
if(!inr[to]&&!sit[to].typ)
{
sit[to].typ=;
Q.push(to);
}
}
}
}
}
int main()
{
init();
dfs();
make_sit();
judge();
add_edge();
bfs();
int T;
scanf("%d",&T);
while(T--)
{
int ty;
scanf("%d",&ty);
for(int i=;i<=;i++)
{
scanf("%d",&ta[i]);
}
for(int i=;i<=;i++)
{
scanf("%d",&tb[i]);
}
sort(ta+,ta+);
sort(tb+,tb+);
ull has=;
if(ty)
{
for(int i=;i<=;i++)
{
has=has*seed+tb[i];
}
for(int i=;i<=;i++)
{
has=has*seed+ta[i];
}
int t=nnum[has];
if(sit[t].typ==)
{
printf("Deal\n");
continue;
}else if(sit[t].typ==)
{
printf("Bob\n");
continue;
}else
{
printf("Alice\n");
continue;
}
}else
{
for(int i=;i<=;i++)
{
has=has*seed+ta[i];
}
for(int i=;i<=;i++)
{
has=has*seed+tb[i];
}
int t=nnum[has];
if(sit[t].typ==)
{
printf("Deal\n");
continue;
}else if(sit[t].typ==)
{
printf("Alice\n");
continue;
}else
{
printf("Bob\n");
continue;
}
}
}
return ;
}
CF919F的更多相关文章
- CF919F A Game With Numbers
题目:(luogu翻译错的很多) Alice和Bob玩游戏,每人有8张牌,牌的值为0~4.每一轮当前玩家选择自己的牌A和对手的牌B,然后将A的值变为( A + B )%5,其中A和B都不是0. 当一个 ...
随机推荐
- window 编译lua 5.3
由于lua 5.1 不支持左移右移的操作符,所以要移植lua 5.3.方便在window 下编译调试 参考链接: http://www.linuxidc.com/Linux/2014-02/96459 ...
- Maven SSH三大框架整合的加载流程
<Maven精品教程视频\day02视频\03ssh配置文件加载过程.avi;> 此课程中讲 SSH三大框架整合的加载流程,还可以,初步接触的朋友可以听一听. < \day02视频\ ...
- (3)java数据结构--有枚举 属性
java中的数据结构 - 南风顾 - 博客园http://www.cnblogs.com/tingxuelou/p/6686143.html 线性表,链表,哈希表是常用的数据结构,在进行Java开发时 ...
- ht学习流程
http://www.hightopo.com/blog/461.html数百个 HTML5 例子学习 HT 图形组件 掌握 HT 基础: 1.先入门手册 http://www.hightopo.co ...
- spring3.2.2 remoting HTTP invoker 实现方式
最近跟朋友聊天,聊到他们现在项目的架构都是把数据层跟应用层分离开来,中间可以加memcached等的缓存系统,感觉挺好的,很大程度上的降低耦合,然后还明确分配了数据层跟应用层任务.也方便定位.找到问题 ...
- Android 正则表达式验证手机号码
方案一:比较精准的判断手机段位,但是随着手机号段的增多要不断的修改正则 public boolean isPhoneNumber1(String phone) { String regExp = &q ...
- Thymeleaf模板布局
⒈定义片段 1.使用th:fragment <div th:fragment="copy"> © 2019 <a href="http://www.co ...
- Python在线聊天软件(Tkinter)
1.python2.7下安装 apt-get install python-tk 主要功能是实现客户端与服务器端的双向通信,这个小东西用到的主要知识点: 1.Python Socket:2.Pytho ...
- 【转】C++标准转换运算符const_cast
const_cast转换符是用来移除变量的const或volatile限定符. 对于const变量,我们不能修改它的值,这是这个限定符最直接的表现.但是我们就是想违背它的限定希望修改其内容怎么办呢? ...
- C++代码风格指南总结
C++代码风格指南 代码风格的重要性 今天我收到thougthwork笔试没过的消息, 心里确实很难受, 然后师兄说我代码写得很糟糕 细想一下, 我写代码确实是随心所欲, 并没有遵循什么规范; 所以现 ...