• hdu 1729


    HDU 1729 Stone Game(经典题)  

    2012-04-04 17:18:08|  分类: 博弈论|举报|字号 订阅

    博弈SG函数问题、寻找必败态
    题目大意:(取石子游戏)有n个箱子,体积为Si,当前箱子里的石子数为Ci。两个人轮流往箱子里放石子,而且每一次放是数量都有限制,不能超过当前箱子内石子数的平方。例如箱子里有3颗石子,那么下一个人就可以放1~9颗石子,直到箱子被装满。当有一方放不下石子时游戏结束,最后放不下石子的人输。
    算法分析:

    PSSG(X)=mex(SG(X->Y1),SG(X->Y2),SG(X->Y3),...........,SG(X->YN));是y1 ,y2 ,y3......的SG值取mex;而不是对(Y1Y2,...YN)取mex;

    http://blog.163.com/scuqifuguang@126/blog/static/171370086201101711276278/这个文章很不错。

    那么对于这题对于每一堆,放石子放满就想当于满的时候取s-c个,反向只是让我理解题意更深。

    首先我们知道(S,S)这个局面是必败局面。

    对于每一堆能加的数量有限,而当c的值(大于或者等于)D=sqrt(s) 或者 D=sqri(s)+1的时候就可以一次完成,就是说可以从当前局面到达(S,S)的局面,所以当前局面是必胜局面。

    而这种情况下,你能造成的局面有集合A={0,1,2,...,s-c-1};因为你可以去s-c,s-c-1,s-c-2,.....,1;那么对应mex(x)函数(即A中未出现的最小的一个数字),那么自然该局面的SG值就是s-c了;

    另外当c的值小于D的时候,是不可能一下子加满的,因为c*c+c绝对是小于s的;那么小于D的局面一定能够是必输的吗?很显然不是的。

    对于(S,D-1)这个局面,一定是必输,因为他能到的局面都是必胜!现在c小于D,那么如果(S,C)这个局面能到(S,D);就代表这个局面是必胜的。所以现在SG值要在新集合(D,C)中求,而求法与上面的相同求新的D,所以可以用递归函数:当C>D时,返回(S-C)

    差不多就是这样。

    其实D = sqrt(s);这里算是个加速,要不然就要:while(d*d+d < S) d++;这样会很慢的。

    思路:这题明显的sg函数。可惜我纠结了半天没想起思路来。设当前的箱子容量为si,求出一个t满足:t + t * t < si,若是当前箱子里有ci颗石头,

             1ci > t 则必胜;

             2ci == t 则必败;

             3ci < t不可立即断定,将t作为si递归调用函数。

    当满足ci > t时,return si - ci 作为当前状况的sg值。因为:

    如图:

    HDU 1729 Stone Game - _眼淚笑了 - _眼淚笑了

    cisi点时,为有向图的端点,出度为0,也就是必败点,所以sg值为0

    ci 位于si - 1时,ci的端点可能的sg值构成的凑集为{0},所以当前sg 1

    ci 位于si - 2 时,ci的端点可能的sg值构成的凑集为{0 1},所以当前的sg值为2

    可得,ci地点地位的sg值为si - ci

    代码如下:
     1 #include<iostream>
     2 #include<string>
     3 #include<cstdio>
     4 #include<vector>
     5 #include<queue>
     6 #include<stack>
     7 #include<set>
     8 #include<algorithm>
     9 #include<cstring>
    10 #include<stdlib.h>
    11 #include<math.h>
    12 using namespace std;
    13 #define ll __int64
    14 int getsg(int s,int d){
    15     int q=(int)sqrt(s*1.0);
    16     while(q+q*q>=s) q--;
    17     if(d>q) return s-d;
    18     else return getsg(q,d);
    19 }
    20 int main(){
    21     int n,cas=0;
    22     while(cin>>n,n){
    23         int ans=0;
    24         while(n--){
    25             int a,b;scanf("%d%d",&a,&b);
    26             if(a==0||b==0||a==b) continue;
    27             ans^=getsg(a,b);
    28         }
    29         printf("Case %d:
    ",++cas);
    30         if(ans) cout<<"Yes"<<endl;
    31         else cout<<"No"<<endl;
    32     }
    33 }
  • 相关阅读:
    Contract
    VS.NET 发布 常识
    案例分析:培训合同与赔款事宜
    DNN最新资源,目前在学SAP,所以.Net的工作就停滞了。
    劳动争议处理依据劳动法
    开发网格应用程序<1>
    BI在中国成了装饰品
    VOIP侵蚀电信利益,未来最赚钱的行业.
    [转]个人知识管理-Web2.0技术下的一个热点
    [转]冲杯三鹿奶粉给你喝!
  • 原文地址:https://www.cnblogs.com/ainixu1314/p/3933663.html
Copyright © 2020-2023  润新知