zoukankan      html  css  js  c++  java
  • POJ 2348 Euclid's Game 博弈论

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

    顺便说,必应翻译真的好用,比谷歌翻译好用100倍。

    很难判断这道题的具体博弈类型。

    有两种写法,一种是找规律,一种是推理得到关系后循环(或递归)处理。两种写法都能在题目下面的discuss中找到。

    1.找规律,我在这里直接复制了discuss中大神算出的sg函数表(在考试中这种写法是很值得借鉴的,这里就体现出代码能力的重要了,找规律天下第一!)。

    我算了一下前 30 × 30 的 Sprague-Grundy 函数表,如下:
    
          0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 
       -----------------------------------------------------------------------------------------------
     0 |  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
     1 |  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 
     2 |  0  2  1  0  2  1  3  3  4  4  5  5  6  6  7  7  8  8  9  9 10 10 11 11 12 12 13 13 14 14 15 
     3 |  0  3  0  1  0  1  2  1  2  3  2  3  4  4  4  5  5  5  6  6  6  7  7  7  8  8  8  9  9  9 10 
     4 |  0  4  2  0  1  0  0  1  2  1  1  2  3  2  3  3  4  3  4  4  5  5  5  5  6  6  6  6  7  7  7 
     5 |  0  5  1  1  0  1  0  0  0  1  2  1  2  2  2  3  2  3  3  3  4  3  4  4  4  5  4  5  5  5  6 
     6 |  0  6  3  2  0  0  1  0  0  0  1  1  2  1  1  1  2  2  3  2  2  3  3  3  4  3  4  4  4  4  5 
     7 |  0  7  3  1  1  0  0  1  0  0  0  0  1  1  2  1  1  2  2  2  2  3  2  2  3  3  3  3  4  3  4 
     8 |  0  8  4  2  2  0  0  0  1  0  0  0  0  1  1  1  2  1  1  1  1  2  2  2  3  2  2  3  3  3  3 
     9 |  0  9  4  3  1  1  0  0  0  1  0  0  0  0  0  1  1  1  2  1  1  1  2  2  2  2  2  3  2  2  2 
    10 |  0 10  5  2  1  2  1  0  0  0  1  0  0  0  0  0  0  1  1  1  2  1  1  1  2  1  2  2  2  2  3 
    11 |  0 11  5  3  2  1  1  0  0  0  0  1  0  0  0  0  0  0  1  1  1  1  2  1  1  1  1  2  2  2  2 
    12 |  0 12  6  4  3  2  2  1  0  0  0  0  1  0  0  0  0  0  0  0  1  1  1  1  2  1  1  1  1  1  1 
    13 |  0 13  6  4  2  2  1  1  1  0  0  0  0  1  0  0  0  0  0  0  0  0  1  1  1  1  2  1  1  1  1 
    14 |  0 14  7  4  3  2  1  2  1  0  0  0  0  0  1  0  0  0  0  0  0  0  0  1  1  1  1  1  2  1  1 
    15 |  0 15  7  5  3  3  1  1  1  1  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  1  1  1  1  1  2 
    16 |  0 16  8  5  4  2  2  1  2  1  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  1  1  1  1  1 
    17 |  0 17  8  5  3  3  2  2  1  1  1  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  1  1  1 
    18 |  0 18  9  6  4  3  3  2  1  2  1  1  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  1 
    19 |  0 19  9  6  4  3  2  2  1  1  1  1  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0 
    20 |  0 20 10  6  5  4  2  2  1  1  2  1  1  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0 
    21 |  0 21 10  7  5  3  3  3  2  1  1  1  1  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0 
    22 |  0 22 11  7  5  4  3  2  2  2  1  2  1  1  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0 
    23 |  0 23 11  7  5  4  3  2  2  2  1  1  1  1  1  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0 
    24 |  0 24 12  8  6  4  4  3  3  2  2  1  2  1  1  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0 
    25 |  0 25 12  8  6  5  3  3  2  2  1  1  1  1  1  1  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0 
    26 |  0 26 13  8  6  4  4  3  2  2  2  1  1  2  1  1  1  0  0  0  0  0  0  0  0  0  1  0  0  0  0 
    27 |  0 27 13  9  6  5  4  3  3  3  2  2  1  1  1  1  1  0  0  0  0  0  0  0  0  0  0  1  0  0  0 
    28 |  0 28 14  9  7  5  4  4  3  2  2  2  1  1  2  1  1  1  0  0  0  0  0  0  0  0  0  0  1  0  0 
    29 |  0 29 14  9  7  5  4  3  3  2  2  2  1  1  1  1  1  1  0  0  0  0  0  0  0  0  0  0  0  1  0 
    30 |  0 30 15 10  7  6  5  4  3  2  3  2  1  1  1  2  1  1  1  0  0  0  0  0  0  0  0  0  0  0  1 

    可以发现0的分界线在黄金分割比附近,直接算一个边界就可以了。边界不好看清的话可以上下拖动滚动条。(我大概有病orz,不过真的能看见,希望大家试试)

    代码

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<cmath>
     5 #include<iostream>
     6 #include<map>
     7 #include<ctime>
     8 using namespace std;
     9 const int maxn=1<<13;
    10 int n;
    11 int main(){
    12     long long x,y;
    13     while(~scanf("%lld%lld",&x,&y)){
    14         if(x==0&&y==0)break;
    15         if(x>y)swap(x,y);
    16         long long w=((double)x*2.0/(sqrt(5.0)-1.0));
    17         if(y<=w&&y!=x)printf("Ollie wins
    ");
    18         else printf("Stan wins
    ");
    19     }
    20     return 0;
    21 }
    View Code

    1.根据规则进行推理,在日常写题还是很推荐这种写法的,毕竟博弈论能找到规律的毕竟只是一部分,大部分不用dp的博弈论都是相应对策或者必胜选择的推理(个人感受不一定对)。

    记每次一个人开始操作前的两数大的为y,小的x。

    全程两人都没有选择(每次的情况都满足y减去一次x就比x小)的时候结果是一定的,那么直接循环找出最终胜利者就可以了(显然满足这种条件的时候循环次数不会太多)。

    假如其中有一个人有选择(y可以减n次小的才比x小,n>1)的时候,这个人如果在y中取n个x时必输,这个人就可以在y中取n-1个x,那么对方就必输。

    于是有了一个循环解决的写法。

    代码

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<cmath>
     5 #include<iostream>
     6 #include<map>
     7 #include<ctime>
     8 using namespace std;
     9 const int maxn=1<<13;
    10 int n;
    11 int main(){
    12     long long x,y;
    13     while(~scanf("%lld%lld",&x,&y)){
    14         if(x==0&&y==0)break;
    15         if(x>y)swap(x,y);
    16         int w=0;
    17         while(x!=0){
    18             if(y%x==0||y-x>x)break;
    19             y-=x;
    20             w^=1;
    21             if(x>y)swap(x,y);
    22         }
    23         if(w)printf("Ollie wins
    ");
    24         else printf("Stan wins
    ");
    25     }
    26     return 0;
    27 }
    View Code
  • 相关阅读:
    Lintcode: Two Strings Are Anagrams
    Leetcode: House Robber
    Leetcode: Binary Tree Right Side View
    Leetcode: Number of Islands
    Lintcode: Subarray Sum
    Lintcode: Sort Letters by Case
    Lintcode: Sort Colors II
    Lintcode: Single Number III
    Lintcode: Search Range in Binary Search Tree
    Lintcode: Binary Tree Serialization (Serialization and Deserialization Of Binary Tree)
  • 原文地址:https://www.cnblogs.com/137shoebills/p/8252875.html
Copyright © 2011-2022 走看看