POJ 1740 A New Stone Game【博弈】

来源:互联网 发布:windows 2008 r2 编辑:程序博客网 时间:2024/04/30 07:04

题目链接:

http://poj.org/problem?id=1740

题意:

n堆棋子,每次可以从一堆中拿出任意多个任意分配到其他非0的堆中,谁没有棋子可拿谁输,问先手是否赢。

分析:

好智障啊,题意就没搞清楚,一直以为是拿出来的只能放到一个非0堆中,其实是随意放在任何非0堆中!这很关键!
博弈的关键做出对称状态后再完全模仿对手
后手如果有条件完全模仿先手的状态,那么后手一定会赢。
如果堆数为偶数,我们分析如何会出现对称状态,如果本身就是对称的状态,如a,a,b,b,c,c...,后手完全模仿先手的操作,那么后手必赢。否则,先手可以来制造对称状态,即先手可以将最大的一堆中的石子平分给其他堆使得堆中棋子两两相等,面对对称状态,此时先手变为了后手,必赢。
如果堆数为奇数,从最大堆中拿出一部分,填补剩下的所有堆,使得两两相等。剩下的拿走。先手必赢。
那么问题来了,将最大的一堆中的石子平分给其他堆使得堆中棋子数两两相等,这个一定可以实现么?
首先所有堆按照棋子个数排个序,则有an=a0+(a1a0)+(a2a1)+...(anan1),那么两两一组,组内元素之差的累加和一定小等于最大堆比最小堆多的数目。偶数堆石子之和为偶数,有这两个条件,便可以保证可以实现堆中元素两两相等了。

代码:

/*************************************************************************    > File Name: 1740.cpp    > Author: jiangyuzhu    > Mail: 834138558@qq.com     > Created Time: 2016/7/30 11:27:53 ************************************************************************/#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<vector>#include<set>#include<map>#include<algorithm>using namespace std;const int maxn = 10 + 5;int a[maxn];int main (void){    int n;    while(scanf("%d", &n) && n){        bool flag = true;        for(int i = 0; i < n; i++){            scanf("%d", &a[i]);        }        if(n & 1) puts("1");        else{            sort(a, a + n);            for(int i = 0; i < n; i += 2){                if(a[i] != a[i + 1]){                    puts("1");                    flag = false;                    break;                }            }            if(flag) puts("0");        }    }    return 0;}
0 0