标签:lan 就是 else space swa 斐波那契数 turn map res
n = m + 1
,那么后手必胜,有如下结论:
n = ( m + 1 ) * r + s
其中(r > 1,0 <= s < m + 1)
。如果s=0的话,先手每次取k个,后手只要取(m+1-k)个即可,后手必赢。如果s!=0的话,先手者第一次取s个,后手第一次取k个,接下来先手只要取(m + 1 - k)
个即可,先手必赢。假设有n堆石子,每堆石子分别有\(a_1, a_2,…,a_n\)个,每次可以选择任意一堆且至少取1枚石子,
甲乙两个玩家轮流取石子, 最后把石子取完的人获胜, 保证甲乙每一步的决策都是最优的, 甲为先手操作, 问甲胜还是乙胜。
1):考虑极端情况, 当\(a_1, a_2,a_3...a_n\)全为0时, res = 0, 此时先手无法操作, 先手必败。
2):当\(res = x ≠ 0\)时,我们取\(res\)最高位\(1\),设最高位\(1\)为第\(k\)位,则一定存在\(a_i\)第\(k\)位为\(1\)。对于此,我们可以反证, 若\(a\)中所有第\(k\)位全为\(0\), 则\(res\)第\(k\)位也为\(0\), 矛盾。
所以我们让\(a_i\land x, 我们可以得出\)\(a_i\land x < a_i\)。那么我们完全可以从\(a_i\)中拿走\(a_i\land x\), 即\(a_i -a_i\land x>0\), 最后剩下\(a_i -(a_i -a_i\land x) = a_i\land x\);
此时后手的局面为\(res = a_1 \land a_2\land ,…,\land a_i\land x,…,\land a_n=x\land x=0\)
即先手通过一定的操作让后手面对\(res=0\)的情况。
?3): 当\(res = x = 0\)且\(a\)不全为\(0\)时, 我们无法通过任何操作让下一个状态的\(res\)也为\(0\)。这一步的证明可以采用反证法。
?若存在$a_i \(拿走一定的石子变为\)a_i^′$且res不变。
?在目前状态\(res= a_1\land a_2\land ,…,\land a_i\land ,…,\land a_n=0\)设为①式。
?在下个状态\(res= a_1\land a_2\land ,…,\land a_i^′\land ,…,\land a_n=0\)设为②式。
?$①\land ② $= \(0\land 0\) => $ a_i\land a_i^′ = 0$
?即\(a_i= a_i^′\), 与假设矛盾。
综上所述, 当\(a\)不全为\(0\)时, 任意一个\(res != 0\)的局面, 先手可以通过一定的操作让后手面对\(res = 0\)的局面。
对于任意一个\(res = 0\)的局面, 先手无法通过任何操作让后手面对\(res = 0\)的局面。
得出结论, 当\(res = 0\)时先手必败, 反之必胜。
问题描述: 有一个\(n\)级台阶的楼梯, 每级台阶上有若干个石子, 其中第i级台阶上有\(a_i\)个石子\((i≥1)\)。两位玩家路轮流操作, 每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶上(不能不拿)。
已经拿到地面的石子不能再拿, 最后无法进行操作的人视为失败。
问如果两人都采取最优策略, 先手是否必胜.
1): 考虑极端情况, 当\(a_1, a_3,…,a_n\)全为0时, \(res = 0\), 此时先手只能将偶数级台阶往下搬, 后手只需要将先手从偶数级台阶上搬下来的石子全部搬到下一级偶数级台阶, 先手必败。
2): 当\(res = x ≠ 0\)时, 通过经典Nim游戏的证明, 我们知道一定有一种方法搬一定的石子到下一级让后手面对res为0的局面。
3):当\(res = x = 0\)且\(a\)不全为\(0\)时, 我们无法通过任何操作让下一个状态的\(res\)也为\(0\)。
即对于\(res\)不为\(0\)的情况, 先手总能通过一定的操作让后手面对\(res\)为\(0\)的情况,。
然而\(res\)为\(0\)时, 先手无论做什么操作都无法让后手面对\(res\)为\(0\)的情况。
那么此刻我们就将题目转化为在奇数台阶上的经典Nim游戏。
为什么不用\(res= a_2\land a_4\land a_6\land ,…,\land a_n=0\)(n为偶数)来判定胜负?
hdu_1850(经典Nim)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100 + 10;
int n, a[maxn], res;
int main()
{
while(cin >> n)
{
if(!n) break;
res = 0;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
res ^= a[i];
}
if(res == 0) puts("0");
else
{
int ans = 0;
for(int i = 1; i <= n; i++)
{
if((res ^ a[i]) < a[i]) ans ++;
}
cout << ans << endl;
}
}
return 0;
}
hdu_1730(经典Nim)
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n, m;
while(cin >> n >> m)
{
int res = 0;
for(int i = 1; i <= n; i++)
{
int a, b; cin >> a >> b;
res = res ^ (abs(a - b) - 1);
}
if(res == 0) puts("BAD LUCK!");
else puts("I WIN!");
}
}
poj_1704(台阶Nim)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1000 + 10;
int a[maxn], d[maxn];
int main()
{
int T; cin >> T;
while(T--)
{
int n; cin >> n;
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
for(int i = 1; i <= n; i++)
d[i] = a[i] - a[i - 1] - 1;
int res = 0;
for(int i = n; i > 0; i -= 2)
res ^= d[i];
if(res) puts("Georgia will win");
else puts("Bob will win");
}
return 0;
}
hdu_4315(台阶Nim)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
int a[maxn];
int n, k;
int main()
{
while(cin >> n >> k)
{
memset(a, 0, sizeof a);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
if(k == 1) puts("Alice");
else
{
int res = 0;
if(n & 1)
{
if(k == 2) res ^= a[1] - 1;
else res ^= a[1];
for(int i = 3; i <= n; i += 2)
res ^= a[i] - a[i - 1] - 1;
}
else
{
for(int i = 2; i <= n; i += 2)
res ^= a[i] - a[i - 1] - 1;
}
if(res) puts("Alice");
else puts("Bob");
}
}
return 0;
}
两堆石子各有若干个, 两人轮流从一堆取至少一个石子或从两堆取同样多的物品, 最后一名取完石子者胜利。
?首先考虑最(zhao)极(gui)端(lv)的情况, (0, 0), (1, 2), (3, 5)局面为先手必败局面。而且这样的数字对被称为奇异局势。
奇异局势的定义如下:
接下来我们看奇异局势的几个性质:
性质1: 任何自然数都包含在一个且仅有一个奇异局势中。
性质2: 任意操作都能将奇异局势转变为非奇异局势.
性质3: 采取适当的方法, 可将非奇异局势转变为奇异局势。
证明略
hdu_2177
#include<bits/stdc++.h>
using namespace std;
int n, m;
bool check(int n, int m)
{
int x = min(n, m), y = max(n, m);
double c = (sqrt(5.00000) + 1) / 2;
double d = (double)(y - x);
if(x == int(c*d)) return 1; // 必败
return 0;
}
void work()
{
if(n > m) swap(n, m); // (n, m)
//第一个模块 我们能一起减去让他成为必败态
{
int tem = m - n;
double c = (sqrt(5.00000) + 1) / 2;
int a = double(tem) * c;
int b = a + tem;
if(n - a == m - b) cout << a << " " << b << endl;
}
//第二个模块 我们求出当前n的奇异局势, 如果m比他大 拿走就行
//如果m比他小我们求出(x, n) 然后拿走m
{
double c = (sqrt(5.00000) + 1) / 2;
int x = n;
double d = x / c;
int y = n + d;
if(m > y) cout << x << " " << y << endl;
else
{
double x = double(n) * 2 / (sqrt(5.000000) + 1);
cout << int(x) << " " << n << endl;
}
}
}
int main()
{
while(cin >> n >> m)
{
if(!(n + m)) break;
if(check(n, m)) puts("0");
else
{
puts("1");
work();
}
}
return 0;
}
一堆石子有\(n\)个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完。以后每次取的石子数不能超过上次取子数的\(2\)倍。取完者胜。给定\(n\),问先手必胜还是必败。
采用归纳法证明:
首先: 将\(n\)记为\(f(i)\);
我们考虑最极端的情况当\(n=1\)时, 先手无法全部取完, 必败; \(n=2\)时, 先手只能取一枚石子, 必败。同时对于任意一个\(n\)先手都不能取\(n/3\)以上的石子, 因为这样先手也将必败。
假设\(i≤k\)时, 结论成立, 当\(i=k+1\)时,$ f(i)=f(k)+f(k-1)$
首先我们明确, 对于\(fibonacci\)数列, 有如下几个不等式:
我们可以将\(f(i)\)分解为\(f(k)\)和\(f(k-1)\)两堆石子。基于第一个不等式, 我们得知先手无法一次拿完所有的\(f(k-1)\)。同时基于假设, 在f(k-1)中先手必败, 则后手将取完所有石子同时余下\(f(k)\)个石子。
对于余下的\(f(k)\)个石子, 由不等式三可以得出先手无法全部拿光\(f(k)\), 根据假设可知\(f(k)\)先手必败, 所以总局面先手必败。
当\(n\)不为\(fibonacci\)数时, 根据\(Zeckendorf\)定理(齐肯多夫定理)(任意一个数字都可表示为若干个不连续的斐波那契数之和), 先手可取一定的石子让后手面对\(fibonacci\)数
例题:
hdu_2516
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
unordered_map<int, int> mp;
ll f[50];
void fib()
{
f[0] = f[1] = 1;
for(int i = 2; i <= 50; i++)
{
f[i] = f[i - 1] + f[i - 2];
mp[f[i]]++;
}
}
int main()
{
int n;fib();
while(cin >> n)
{
if(n == 0)break;
if(!mp[n]) puts("First win");
else puts("Second win"); //如果是fibonacci数, 则先手必败
}
return 0;
}
hdu_1524
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
int n, num;
int sg[maxn];
int head[maxn], ver[maxn], nex[maxn], tot;
void add(int x, int y)
{
ver[++tot] = y; nex[tot] = head[x]; head[x] = tot;
}
int GetSg(int x)
{
if(sg[x] != -1) return sg[x];
bool vis[maxn];
memset(vis, 0, sizeof(vis));
for(int i = head[x]; i; i = nex[i]) // 扫描所有出边
{
int y = ver[i];
sg[y] = GetSg(y);
vis[sg[y]] = 1; //所有出边的sg函数值
}
for(int i = 0; i < n; i++)
if(!vis[i]) return sg[x] = i; // mex运算
return 0;
}
void init()
{
memset(head, 0, sizeof(head));
memset(nex, 0, sizeof nex);
memset(ver, 0, sizeof ver);
memset(sg, -1, sizeof sg);
tot = 0;
}
int main()
{
while(cin >> n)
{
init();
for(int i = 0; i < n; i++)
{
cin >> num;
while(num--)
{
int x; scanf("%d", &x);
add(i, x);
}
}
while(cin >> num)
{
if(!num) break;
int res = 0;
while(num--)
{
int x; scanf("%d", &x);
res ^= GetSg(x);
}
if(res) puts("WIN");
else puts("LOSE");
}
}
return 0;
}
hdu_1536
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 10;
int s[maxn], sg[maxn];
int k;
void init()
{
memset(sg, -1, sizeof(sg));
}
int GetSg(int x)
{
if(sg[x] != -1) return sg[x];
bool vis[maxn]; memset(vis, 0, sizeof(vis));
for(int i = 1; i <= k; i++)
if(x >= s[i])
{
sg[x - s[i]] = GetSg(x - s[i]);
vis[sg[x - s[i]]] = 1;
}
for(int i = 0; ; i++)
if(!vis[i]) return sg[x] = i;
return 0;
}
int main()
{
ios::sync_with_stdio(false);
while(cin >> k)
{
init();
if(k == 0) break;
for(int i = 1; i <= k; i++) cin >> s[i];
int num; cin >> num;
while(num--)
{
int x, res = 0; cin >> x;
for(int i = 1; i <= x; i++)
{
int y; cin >> y;
res ^= GetSg(y);
}
if(res) cout << "W";
else cout << "L";
}
cout << endl;
}
return 0;
}
标签:lan 就是 else space swa 斐波那契数 turn map res
原文地址:https://www.cnblogs.com/zxytxdy/p/11619871.html