zoukankan      html  css  js  c++  java
  • 博弈论(Game Theory)

    博弈论(Game Theory)

    首先先说两个定义
    N状态:前面的一个玩家必胜
    P状态: 后面一个玩家必胜

    巴什博弈 (Bush Game)

    有一堆数量为n的物体,轮流拿,至少拿1个,至多拿k个(N>K);
    如果n%(k+1)==0,那么先手必败。
    这一切是显而易见,毫无疑问的

    一道例题
    Tang and Jiang are good friends. To decide whose treat it is for dinner, they are playing a game. Specifically, Tang and Jiang will alternatively write numbers (integers) on a white board. Tang writes first, then Jiang, then again Tang, etc... Moreover, assuming that the number written in the previous round is X, the next person who plays should write a number Y such that 1 <= Y - X <= k. The person who writes a number no smaller than N first will lose the game. Note that in the first round, Tang can write a number only within range [1, k] (both inclusive). You can assume that Tang and Jiang will always be playing optimally, as they are both very smart students.

    这一道题目大意上和Bush Game 差不多,但是不同的是它里面说的是写不出不小于n的就输了,那么必胜的时候就是你已经写出了n-1的时候。所以此时如果(n-1)%(k+1)==0那么先手必定输,否则就是后手输,因为先手那一次后可已转换为第一种情况

    尼姆博弈 (Nim Game)

    有n堆物体,每一堆的数量为a[i]个,每一次一个人任选一堆取出任意个(不能为0)
    设k为每一堆异或的结果,如果k==0,那么先手必定失败,否则先手必定胜利。
    这样来想,把每一堆的数量转换成二进制然后竖着来最低位对齐。如果说k==0,那么这意味这每一个列的1的个数一定会是偶数个。而第一个人取走了一些以后呢,这意味着一定一些列的1的个数为变成奇数个,那么k一定就不再等于0了。而此时后手的人只要把异或的值修正为0就可以了。
    因为k不等于0了,那么这就说明k的最高位一定会是1,而这个最高位的1一定会是a[i]中的对应位上的1提供的
    而根据异或的性质,k和a[i]异或结果为其他的异或结果。
    我们可以在a[i]中减去一些值,是a[i]最终等于k和a[i]异或的结果,又因为k最高位的1是由a[i]提供的,那么那里一定会变成0,就一定会比a[i]小,而只要把这里减掉,k就又会等于0了

    SG函数

    公平组合游戏

    1.双方交替来进行;
    2.游戏进行的任意时刻,可以执行的合法行动与哪一个玩家执行无关;
    3.当玩家无法行动的时候,就判负

    mex运算

    [mex(S)=min{x|xin N,x otin S} ]

    SG函数

    对于任意状态下的x

    [SG(x)=mex{SG(y)|y是x的后继状态} ]

    对于终止状态,SG值为0。

    如果某一状态后继SG有0,则当前状态为N
    如果当前状态所有后继SG不为0,则当前为P;

    好的,既然已经知道了SG函数是什么,那么就有一道题了。

    移棋子游戏

    Description

    给定一个有N个节点的DAG图,图上某些节点上面有棋子。两名玩家交替移动棋子,玩家每一次可以将任意一颗棋子沿着有向边移动到下一个节点,当无法移动的时候,就输掉了游戏,假设双方都足够聪明,问先手必胜还是后手必胜。

    Input

    第一行三个整数N,M,K,表示N个节点M条边K个棋子。接下来M行,每行两个整数x,y,代表x节点到y节点的有向边再接下来K行,表示K个棋子所在的节点编号。

    output

    先手胜输出"win",否则输出"lose"

    Sample Input

    6 8 4
    2 1
    2 4
    1 4
    1 5
    4 5
    1 3
    3 5
    3 6
    1 2 4 6
    

    Sample Output

    win
    

    这应该是我第一次这么抄题吧。
    DAG图,看到这个应该回想起拓扑排序,其次,因为没有后继的点SG值为0,而每一个点的SG值又是有它的后继决定的。如果它的后继没有弄完,那么就不可以算。这很像拓扑排序把入度为0的点push进队列。
    因此不难想到这一题可以用类似于拓扑排序的方法来做。我们需要存两个图,正向的和反向的。正向的图用来寻找这一个点的后继获取这一个点的SG值,反向的用来找祖宗,修改祖宗的出度,并把出度为0的祖宗push进入队列
    因为有拓扑排序的基础,所以这一道题可以比较容易的做出来了

    #include<bits/stdc++.h>
    #define maxn 2003
    using namespace std;
    queue<int>Q;
    vector<int>G[maxn];//正着存图
    vector<int>G_[maxn];//反着存图
    int chess[maxn],SG[maxn];//记录棋子的位置 ,SG函数值 
    bool vis[maxn*3];
    int n,m,outdgr[maxn];//记录点,边,出度 
    int k; 
    
    void solve(){
    	for(int i=1;i<=n;i++){
    		if(!outdgr[i]){
    			Q.push(i);
    		}
    	}
    	
    	while(!Q.empty()){	
    		memset(vis,0,sizeof(vis));	
    		int u=Q.front();Q.pop();
    		vector<int>::iterator iter=G[u].begin();
    		int maxsg=0;
    		while(iter!=G[u].end()){
    			maxsg=max(maxsg,SG[*iter]);
    			vis[SG[*iter]]=true;
    			iter++;
    		}
    		int j;
    		for(j=0;j<=maxsg+1;j++)if(!vis[j])break;
    		SG[u]=j;//得到这一个点的SG值 
    		vector<int>::iterator iter_=G_[u].begin();
    		while(iter_!=G_[u].end()){
    			outdgr[*iter_]--;
    			if(!outdgr[*iter_])Q.push(*iter_);
    			iter_++;
    		}
    	}
    	int ans=SG[chess[1]];
    	for(int i=2;i<=k;i++)ans^=SG[chess[i]];
    	if(!ans)printf("lose
    ");
    	else printf("win
    ");
    	return ; 
    }
    
    int main(){
    	memset(outdgr,0,sizeof(outdgr));
    	scanf("%d%d%d",&n,&m,&k);
    	for(int i=1;i<=m;i++){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		G[u].push_back(v);
    		G_[v].push_back(u); 
    		outdgr[u]++;
    	}
    	for(int i=1;i<=k;i++){
    		scanf("%d",&chess[i]);
    	}
    	solve();
    	return 0;
    } 
    

    那么,That's all.

  • 相关阅读:
    npm
    React
    php区分new static 和new self
    tiny java web server
    算法可视化
    在线markdown编辑器
    JAVA
    linux find命令
    自定义windows新建菜单
    floyd算法
  • 原文地址:https://www.cnblogs.com/perisino/p/10342682.html
Copyright © 2011-2022 走看看