该题一看便是属于博弈的题目,题目中博弈的过程写的很清晰,所以我的第一想法便是模拟这个博弈的过程,按照题目意思来写代码即可
法1:模拟
我这里是通过一个变量来判断博弈谁输谁赢的,因为如果经过偶数次博弈之后结束博弈的话,相当于最后的1在爱丽丝手里,所以爱丽丝输了,反之,如果经过奇数次博弈,相当于最后一次结束在Bob手里,Bob输了
class Solution {
public://模拟
bool divisorGame(int n) {
int sign=0,x;//用sign来记录进行了几次博弈
while(n!=1)//
{
for(x=1;x<n;x++)//寻找因子
{
if(n%x==0)
{
break;
}
}
n=n-x;//改变n
sign++;//如果经过偶数次博弈之后结束博弈的话,相当于最后的1在爱丽丝手里,所以爱丽丝输了,
}//反之,如果经过奇数次博弈,相当于最后一次结束在Bob手里,Bob输了
if(sign%2==0)//判断sign
{
return false;
}
else
{
return true;
}
}
};
法2:动态规划(动态规划还是难啊)
老规矩按照入门动态规划之三步走
第一步我们先定义数组元素及其涵义
我们定义一个一维数组,用下标代表n,用该下标代表的值 代表此时n谁先手谁赢或输
第二步找数组各元素之间的关系(难)
对于每一个n,我们只要能够找到一种情况,即f[ i - j ]为输,就是传给Bob的时候是输的,我们的
f[ i ] 就可以通过一直选1来维持对手选到的的输,所以f[ i ]便是赢
反之如果对手f[ i - j ]是赢的话,那么它也可以通过一直选1来维持自己的赢
class Solution {
public:
bool divisorGame(int n) {
vector<int> f(n + 5, false);//初始化
//f[i] 表示当前数字 i 的时候先手是处于必胜态还是必败态,true 表示先手必胜,false 表示先手必败
f[1] = false;
f[2] = true;//初始化
for (int i = 3; i <= n; ++i) {
for (int j = 1; j < i; ++j) {//遍历每个因子
if (i % j == 0 && !f[i - j]) {//f[i-j]的i-j是传给对手的数字,如果f[i-j]是true,爱丽丝就输了
f[i] = true;
break;//只要有一种机会,爱丽丝就不会输
}
}
}
return f[n];
}
};
实际上,对于该题而言,动态规划的效率反而不高
法3:归纳法
该题题意告诉我们,谁最后拿到1谁就输了,进一步说就是谁拿到2谁就赢了,那么我们可以这样推理:
如果爱丽丝先手拿到的n是个奇数,那么不管怎么拿因子对手拿到的都一定是偶数(因为奇数的因子一定是奇数或者是1,此时经过运算n=n-x,对手拿到的一定是偶数),这样爱丽丝就输了;如果爱丽丝先手拿到的n是个偶数,那么爱丽丝可以通过一直拿1作为因子来维持自己拿偶数(此时对手拿到奇数,而同理根据上面推理可知,此时返回爱丽丝手里必为偶数,就这样循环),爱丽丝就必赢!
综上所述,谁拿偶数谁赢,谁拿奇数谁输
class Solution {
public:
bool divisorGame(int n) {
return n % 2 == 0;
}
};
有时候,动动笔在纸上画一画就能发现规律!!!