http://acm.hdu.edu.cn/showproblem.php?pid=1525
HDU 1525
题意:给定两个正整数a,b。每次操作,可以将大的数减掉小的数的整数倍(减去后不能为负数)。如果一个操作者,将a,b中一个数变成0时游戏结束,并且该玩家胜利。求先手必胜还是后手必胜。
分析:不妨设a>b. ① 若a%b==0先手必胜。
② 若a/b >=2且a%b!=0,即a=b*k+rest ( k>=2 ) ,先手总是能够使其中一方变成( b,a%b )的结果;
若( b,a%b)为必胜态,则先手操作使对手面临(a%b+b,b)的状态,然后自己就一定面对胜态(b,a%b);
若( b,a%b) 为必败态,则先手操作使对手面临(b,a%b) 的状态,自己获胜。
③ 2b>a>b ,则a =b+rest,先手只能操作变成 (b,a%b),然后 后手按照①②③操作判断,谁先到达必胜态谁就赢
<span style="font-size:14px;">#include <iostream>
using namespace std;
int main()
{
long long a,b;
while(cin>>a>>b){
bool tag=true;
if(a==0&&b==0) break;
if(a<b)
swap(a,b);
while(1)
{
if(b==0||a%b==0||a/b>=2) break;
int t=a%b;
a=b;
b=t;
tag=!tag;
}
if(tag)
cout<<"Stan wins"<<endl;
else cout<<"Ollie wins"<<endl;
}
return 0;
}</span>
http://poj.org/problem?id=1740
poj 1740
有n堆石子,每堆中的数量分别为a1,a2,……,an两个人轮流操作,每次操作分两步,第一步从某堆中去掉至少一个,
第二步,可以把该堆剩余石子的一部分分给其它的某些堆(可省略)。当一个人面临所有堆中的石子都为0时,则他就输了。任意输入n堆,和n堆中石子的个数,分析先手胜,还时后手胜。
分析:
① n=1时,先手胜。
② n=2时,如果要想自己赢,则要把对方逼到面临(1,1)的情况,无论对手怎么操作,自己一定赢。
当a1=a2时,先手先操作,后手模仿前者采取一样的策略,一定能使某一状态下先手面临 (1,1)的情况,故后手必胜。即遇到两堆相等的情况下,对先手来说是必败态。
当a1!=a2, 先手一定能使两堆的数量变成相等,使后手面临必败态。先手必胜。
③n=3时,先手操作其中的一堆,使该堆数量为0,且使另外两堆数量相等,使后手面临必败态。先手必胜。
④n>=4时,按从小到大的顺序排序
一 、 若n为偶数,分两种情况
1. n堆可以分成 n/2 对两两数量相等的堆,这样后者总是模仿先手在同数量的堆中采取一样的措施,后手必胜。先手必败。
2. n堆不能分成上述两两相等的堆,取最大的一堆,使其数量与数量最小的一堆相等,同时用高度差平衡其他堆,使其他的堆变成两两相等的堆,注意一定是可以平衡的。因为把剩余的堆相邻两两的差值投射到y轴上发现这些离散的线段和
*小于最高堆相对于最小堆的差值。即转变成必败态,先手胜。
二、 若n为奇数,则取最大的那一堆来平衡其他堆(使其他堆变成 两两相等的堆),并使自身变为0,即转变成必败态,故先手胜。
#include <iostream>
#include <algorithm>
#define Max 10000
using namespace std;
int main()
{
int n,i,a[Max];
while(cin>>n)
{
if(n==0) break;
for(i=0;i<n;i++)
cin>>a[i];
if(n==2&&a[0]==a[1])
cout<<"0"<<endl;
else if(n>=4&&n%2==0)
{
int tag=0;
sort(a,a+n);
for(i=0;i<n;i+=2)
{
if(a[i]!=a[i+1]) //判断是否是n/2对两两相同的堆
{
tag=1;
break;
}
}
if(tag==0)
cout<<"0"<<endl;
else cout<<"1"<<endl;
}
else cout<<"1"<<endl;
}
return 0;
}