暑假集训日记——8.5(博弈)

这是一篇没有看完的博客
“ I m p a r t i a l C o m b i n a t o r i a l G a m e s ” ( 以 下 简 称 I C G ) 。 “Impartial Combinatorial Games”(以下简称ICG)。 ImpartialCombinatorialGamesICG
满足以下条件的游戏是 I C G ICG ICG
1、有两名选手;
2、两名选手交替对游戏进行移动( m o v e move move),每次一步,选手可以在(一般而言)有限的合法移动集合中任选一种进行移动;
3、对于游戏的任何一种可能的局面,合法的移动集合只取决于这个局面本身,不取决于轮到哪名选手操作、以前的任何操作、骰子的点数或者其它什么因素; 4、如果轮到某名选手移动,且这个局面的合法的移动集合为空(也就是说此时无法进行移动),则这名选手负。
根据这个定义,很多日常的游戏并非 I C G ICG ICG。例如象棋就不满足条件3,因为红方只能移动红子,黑方只能移动黑子,合法的移动集合取决于轮到哪名选手操作。

定 义 P − p o s i t i o n 和 N − p o s i t i o n 定义P-position和N-position PpositionNposition:
其中 P P P代表 P r e v i o u s Previous Previous N N N代表 N e x t Next Next,直观的说,上一次 m o v e move move的人有必胜策略的局面是 P − p o s i t i o n P-position Pposition,也就是“后手可保证必胜”或者“先手必败”,现在轮到 m o v e move move的人有必胜策略的局面是 N − p o s i t i o n N-position Nposition,也就是“先手可保证必胜”。
更严谨的定义是:
1.无法进行任何移动的局面(也就是 t e r m i n a l p o s i t i o n terminal position terminalposition)是 P − p o s i t i o n P-position Pposition
2.可以移动到 P − p o s i t i o n P-position Pposition的局面是 N − p o s i t i o n N-position Nposition;3.所有移动都导致 N − p o s i t i o n N-position Nposition的局面是 P − p o s i t i o n P-position Pposition

法一:SG打表找规律

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;

///打表
///f[]:可以取走的石子个数
///sg[]:0~n的SG函数值
///hash[]:mex{}
int f[N],sg[N],hash[N];
void getSG(int n,int m)
{
    int i,j;
    memset(sg,0,sizeof(sg));
    for(i=1;i<=n;i++)
    {
        memset(hash,0,sizeof(hash));
        for(j=1;f[j]<=i&&j<=m;j++)
            hash[sg[i-f[j]]]=1;
        for(j=0;j<=n;j++)    //求mes{}中未出现的最小的非负整数
        {
            if(hash[j]==0)
            {
                sg[i]=j;
                break;
            }
        }
    }
}

///如果有多堆,则
/// num=sg[n1]^sg[n2]^sg[n3]^....^sg[nx];
/// if(num==0) 则先手必败

int main()
{
    int n,k,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>f[i];
    }
    getSG(n,m);
    for(int i=0;i<=n;++i) printf("%d %d\n",i,sg[i]);
}

法二: d f s dfs dfs版打表

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;

///dfs
///注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
///n是集合s的大小 S[i]是定义的特殊取法规则的数组
int s[110],sg[10010],n;
int SG_dfs(int x)
{
    int i;

    if(sg[x]!=-1)
        return sg[x];
    bool vis[110];
    memset(vis,0,sizeof(vis));
    for(i=1;i<=n;i++)
    {
        if(x>=s[i])
        {
            SG_dfs(x-s[i]);
            vis[sg[x-s[i]]]=1;
        }
    }
    int e;
    for(i=0;;i++)
        if(!vis[i])
        {
            e=i;
            break;
        }
    return sg[x]=e;
}

///如果有多堆,则
/// num=sg[n1]^sg[n2]^sg[n3]^....^sg[nx];
/// if(num==0) 则先手必败

int main()
{
    int k,m;
    cin>>m>>n;
    memset(sg,-1,sizeof(sg));
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
    }
    SG_dfs(m);
    for(int i=0;i<=m;++i) printf("%d %d\n",i,sg[i]);
}

K M P 算 法 : KMP算法: KMPKMP算法模板

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值