取石子(三)(四)(五)

取石子三

题意:

给你n堆石子,你可以从一堆中拿取任意个石子,在拿完之后你还可以(你也可以不做)对你操作过的石子堆再进行一次操作——从中拿取一些石子放到其他有石子数不为0的石子堆上。

 

题解:

当只有一堆石子的时候那是必胜态N;

 

两堆石子的时候:如果两堆石子数量一样就是必败态P,因为后手可以跟着前手一样在另一堆石子上面操作,最后一个拿石子的肯定是后手

        如果两堆石子数量不一样那就是必胜态N,因为前手可以通过一次操作使两堆石子数量不一样变成一样,就转化成了上面那一种情况

 

三堆石子的时候也是必胜态,因为前手可以一次操作使它变成两堆石子的第一种情况:

例如:三堆石子数量是x<y<z

我们可以先用z把x补齐到y,然后再把z剩下的石子拿走

如果三堆中有两堆直接相同,那就直接拿走不相同的那一堆就可以了

 

四堆石子的时候:如果他们两两相同或者都一样的情况下,那么就符合两堆石子的第一种状态,必败态

        如果他们都不相同

        例如:x<y<z<w

        这个时候可以用w将y补齐到z,之后再让w的石子剩下x个就可以了(可能有人会怀疑w可能在某种情况下补齐完y,就刚好等于x,不能再拿石子了。这种情况是不存在的,因为我们这样操作相当与在问你y+w>x+z吗,显然这是成立的,那么你的怀疑也就不攻自破了)

 

代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #include<algorithm>
 5 using namespace std;
 6 typedef long long ll;
 7 const int maxn=1e2+10;
 8 int v[maxn];
 9 int main()
10 {
11     int n;
12     while(~scanf("%d",&n))
13     {
14         if(!n) break;
15         int q;
16         memset(v,0,sizeof(v));
17         for(int i=1;i<=n;++i)
18         {
19             scanf("%d",&q);
20             v[q]++;
21         }
22         if(n%2)
23         {
24             printf("Win\n");
25             continue;
26         }
27         int flag=0;
28         for(int i=1;i<=100;++i)
29             if(v[i]%2)
30             {
31                 flag=1;
32                 break;
33             }
34         if(flag) printf("Win\n");
35         else printf("Lose\n");
36     }
37     return 0;
38 }
View Code

 

 

 取石子(四)(威佐夫博弈)

参考链接1    

威佐夫博弈问题:

给你两堆石子,你可以在其中一堆中取走任意个,或者在两堆中拿走相同个

结论:

他的失败态是有规律的:从第0个失败态开始(0,0),(1,2),(3,5),(4,7),(6,10),(8,13) (第一个数是第一堆石子剩下的数量,第二个相仿)

它有以下规律:

 第i个失败态的两个数的差值为i。
用a[i]表示失败态中的第一个数,b[i]表示失败态中的第二个数.(i从0开始)。那么a[i]是前面的失败态中没有出现过的最小的整数,b[i] = a[i]+i;(i >= 0)。
1.每个数仅包含在一个失败态中。
2.每个失败态可以转到非失败态。
3.每个非失败态都可以转到一个失败态。
每个失败态中两个数的差值 * 1.618的向下取整就是这个失败态的第一个数。
给两堆石子,求先手输赢,就可以根据这组数是不是失败态来判断先手是否会赢
如果还要求假设先手赢,先手第一次怎么取石子,可以分为同时取和在一堆取,主要是取后的石子为失败态。

 

本题就是模板题,代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<math.h>
 6 using namespace std;
 7 typedef long long ll;
 8 const int maxn=1e2+10;
 9 int main()
10 {
11     int a,b;
12     while(~scanf("%d%d",&a,&b))
13     {
14         if(a<b) swap(a,b);
15         int c=floor((a-b)*(1+sqrt(5))/2);
16         if(c==b)
17             printf("0\n");
18         else printf("1\n");
19     }
20     return 0;
21 }
View Code

 

 

取石子(五)(斐波那契博弈)

参考链接1    参考链接2

题目:

有一堆个数为n的石子,游戏双方轮流取石子,满足:
1)先手不能在第一次把所有的石子取完;
2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)

 

结论:

他的必败态符合斐波那契数,即当石子数量是斐波那契数中的某个时,那么这个时候就是必败态

 

本题即是模板题,代码:

 要注意一点,本题数据是<=2^64,但是斐波那契数没有一个等于2^64,所以这一位不用考虑,开unsigned long long就可以了

 1  
 2 
 3 #include<algorithm>
 4 
 5 #include<string.h>
 6 
 7 #include<stdio.h>
 8 
 9 using namespace std;
10 
11 unsigned long long A[110];
12 
13 int main()
14 
15 {
16 
17     unsigned long long n;
18 
19     A[0]=0,A[1]=1;
20 
21     for(int i=2;i<=100;i++)
22 
23         A[i]=A[i-1]+A[i-2];
24 
25 //    for(int i=2;i<=100;i++)
26 
27 //        printf("%d %llu\n",i,A[i]);
28 
29     while(~scanf("%llu",&n))
30 
31     {
32 
33 //        printf("%llu\n",n);
34 
35         int p=0;
36 
37         for(int i=1;i<=93;i++)
38 
39         {
40 
41             if(A[i]==n)
42 
43             {
44 
45                 p=1;
46 
47                 break;
48 
49             }
50 
51         }
52 
53         if(p)
54 
55             printf("No\n");
56 
57         else
58 
59             printf("Yes\n");
60 
61     }
62 
63 }
View Code

 

posted @ 2019-08-10 09:27  kongbursi  阅读(467)  评论(0编辑  收藏  举报