这次就整理下关于Nim博弈的新收获和之前比赛的一道经典问题,有时间的话在总结下hiho上刷到的好题。(感觉hiho上的题越来越难刷了,但每天还是三人勉强维持不到10题)。
Nim博弈新收获
参考:http://blog.csdn.net/strangedbly/article/details/51137432
1.把原游戏分解成多个独立的子游戏,则原游戏的SG函数值是它的所有子游戏的SG函数值的异或。
即sg(G)=sg(G1)^sg(G2)^...^sg(Gn)。
2.分别考虑没一个子游戏,计算其SG值。
SG值的计算方法:(重点)
(1).可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1);
(2).可选步数为任意步,SG(x) = x;
(3).可选步数为一系列不连续的数,用模板计算。
感觉对于博弈论我还是没法把理解上升上去,看过大多数资料都是直接说有这个定理,sg函数怎么用,可我还是自己无法证明出来为什么sg函数可以用于博弈,为什么上面定理一就是这么求。
但是如果单纯为了做A题的话目前还算太不困难。
hiho#1173 : 博弈游戏·Nim游戏·三
在这一次游戏中Alice和Bob决定在原来的Nim游戏上增加一条规则:每一次行动时,不仅可以选择一堆取走任意数量的石子(至少取1颗,至多取出这一堆剩下的所有石子),还可以选择将一堆石子分成两堆石子,但并不取走石子。比如说有一堆石子为k个,当Alice或者Bob行动时,可以将这一堆石子分成两堆分别为x,y。满足x+y=k,x,y>0。那么增加了这一条规则后,在Alice总先手的情况下,请你根据石子堆的情况判断是Alice会获胜还是Bob会获胜?
其实这道题就用了上面一条定理1,然后打表算出所有sg值输出就可以了。
using namespace std;
int vis[20005];
int sg[20005];
void init()
{
for(int i=1;i<=20000;i++)
{
memset(vis,0,sizeof(vis));
for(int j=0;j<i;j++)
{
int k;
k=sg[j]^sg[i-j];
vis[sg[j]]=1;
vis[k]=1;
}
for(int j=0;j<=20000;j++)
{
if(!vis[j])
{
sg[i]=j;
break;
}
}
}
}
int main()
{
memset(sg,0,sizeof(sg));
init();
int n,x;
int ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
ans^=sg[x];
}
if(!ans) cout<<"Bob"<<endl;
else cout<<"Alice"<<endl;
}
牛客练习赛13 F题 m皇后
在一个n*n的国际象棋棋盘上有m个皇后。一个皇后可以攻击其他八个方向的皇后(上、下、左、右、左上、右上、左下、右下)。对于某个皇后,如果某一个方向上有其他皇后,那么这个方向对她就是不安全的。对于每个皇后,我们都能知道她在几个方向上是不安全的。现在我们想要求出t0,t1,...,t8,其中ti表示恰有i个方向是"不安全的"的皇后有多少个。
其实这个题也算经典问题八皇后之一,我的思路其实也是映射,先利用结构体储存点,然后按照优先y和x的排下序,接下来依次处理,遍历过的点映射到一个数组,分别做i+j,i-j+n....还是看代码吧
来自:https://www.cnblogs.com/ACRykl/p/8586235.html
#include<cstdio>
#include<algorithm>
using namespace std;
struct Node
{
int x,y,num;
}a[100100];
int sum[100100];
int t[10];
int cmp1(Node A,Node B)
{if(A.x==B.x) return A.y<B.y;return A.x<B.x;}
int cmp2(Node A,Node B)
{if(A.y==B.y) return A.x<B.x;return A.y<B.y;}
int cmp3(Node A,Node B)
{if(A.x+A.y==B.x+B.y) return A.x<B.x;return A.x+A.y<B.x+B.y;}
int cmp4(Node A,Node B)
{if(A.x-A.y==B.x-B.y) return A.x<B.x;return A.x-A.y<B.x-B.y;}
int main()
{
int m;
scanf("%d%d",&m,&m);
for(int i=0;i<m;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
a[i].num=i;
}
sort(a,a+m,cmp1);
for(int i=1;i<m;i++)
if(a[i-1].x==a[i].x)
{sum[a[i-1].num]++;sum[a[i].num]++;}
sort(a,a+m,cmp2);
for(int i=1;i<m;i++)
if(a[i-1].y==a[i].y)
{sum[a[i-1].num]++;sum[a[i].num]++;}
sort(a,a+m,cmp3);
for(int i=1;i<m;i++)
if(a[i-1].x+a[i-1].y==a[i].x+a[i].y)
{sum[a[i-1].num]++;sum[a[i].num]++;}
sort(a,a+m,cmp4);
for(int i=1;i<m;i++)
if(a[i-1].x-a[i-1].y==a[i].x-a[i].y)
{sum[a[i-1].num]++;sum[a[i].num]++;}
for(int i=0;i<m;i++)
t[sum[i]]++;
for(int i=0;i<8;i++) printf("%d ",t[i]);
printf("%d\n",t[8]);
return 0;
}
比较晚了,先整理到这吧。下次再整理hiho上那个区间合并