写博客之前还是贴一下广州的天气:
生活在广州的同学们傻傻分不清楚是冬天还是夏天。。。。。。
下面我们进入正题:
题目:
In the "100 game," two players take turns adding, to a running total, any integer from 1..10. The player who first causes the running total to reach or exceed 100 wins.
What if we change the game so that players cannot re-use integers?
For example, two players might take turns drawing from a common pool of numbers of 1..15 without replacement until they reach a total >= 100.
Given an integer maxChoosableInteger
and another integer desiredTotal
, determine if the first player to move can force a win, assuming both players play optimally.
You can always assume that maxChoosableInteger
will not be larger than 20 and desiredTotal
will not be larger than 300.
解析:
1.首先我们来看看原始的"game 100的情况",这种情况下,数字可以重复利用,其中的optimally原则意思是---两则取数的原则都是一样的。如果数字可以重
复利用,那我们就不需要记录那些数字是被使用过 的。那么我们可知道从1-11之间的数的情况,正如题目中分析的。现在我们假设maxChoosableInter=10,
desiredTotal=24,那么我们就会有如下初始表格:
我们知道所有从1-10之间的数字可以重复使用,那么我们的动态规划状态转移方程有:
result(n)=~result(n-d),其中d在[1,maxchoosableInter]之间。因为我们要求当desiredTotal=n时第一个拿数的player能不能获胜的结果无外乎第一
个player首先在[1,maxchoosableInter]之间随机拿一个数,当第一个player拿完之后,问题就变成第二player在[1,maxchoosableInter]之间先拿一个数(但此时
的desiredTotal=n-d)能不能获胜的情况了,如果他不能,则第一个player获胜。只要在result(n-d)之间的任意一个为假,那么先拿的player就可以获
胜。。。。既然可重复利用数字的情况解决了,那我们来解决不可重复利用数字的情况。。。
显然,当数字不可重复利用时,我们需要额外的变量来保存那些数字是被使用过的,可以采用至上而下的DFS+动态规划解决。。对于DFS中的每一个状态
我们要知道的信息无非就两个:
1. 当前状态下,那些数字是被使用过的;
2. 目前的desiredTotal还剩下多少;
对于第一点,我参考的是discuss里的大神的(方法很妙!),第二点就是每次调用的时候减去当前还没有被选择的数。
AC代码:
class Solution {
public:
bool canIWin(int maxChoosableInteger, int desiredTotal) {
int sum = (1 + maxChoosableInteger)*maxChoosableInteger / 2, current_state=0;
if (sum < desiredTotal)return false;
if (desiredTotal <= 0)return true;
vector<int>used(maxChoosableInteger,0);
return search(desiredTotal,used,current_state);
}
bool search(int tar,vector<int>&used,int current_state){
int size = used.size();
if (tar <= 0)return false;
map<int, bool>::iterator iter = has.find(current_state);
if (iter == has.end()){//如果没有
for (int i = 0; i < size; i++){//逐步查找used中的每一个数
if (used[i] == 0){//说明没被使用
used[i] = 1;
int now = pow(2,i);
if (!search(tar - (i + 1), used,current_state+now)){//如果不成功
has.insert(make_pair(current_state, true));
used[i] = 0;
return true;
}
used[i] = 0;
}
}
has.insert(make_pair(current_state, false));
}
iter = has.find(current_state);
return (*iter).second;
}
private:
map<int,bool>has;//代表在int值状态下先拿会赢的集合
};
但是,在做的过程中,以下两条语句(A)和(B)的执行结果很不一样,其中(A)正确,但是(B)错误:
第一种:
bool search(int tar, vector<int>&used){
int value = booltoint(used), size = used.size();
if (tar <= 0)return false;
map<int, bool>::iterator iter = has.find(value);
if (iter == has.end()){//如果没有
for (int i = 0; i < size; i++){//逐步查找used中的每一个数
if (used[i] == 0){//说明没被使用
used[i] = 1;
if (!search(tar - (i + 1), used)){//如果不成功
has.insert(make_pair(value, true));
used[i] = 0;
return true;
}
used[i] = 0;
}
}
has.insert(make_pair(value, false));
}
return false;//(A)
}
第二种:
bool search(int tar, vector<int>&used){
int value = booltoint(used), size = used.size();
if (tar <= 0)return false;
map<int, bool>::iterator iter = has.find(value);
if (iter == has.end()){//如果没有
for (int i = 0; i < size; i++){//逐步查找used中的每一个数
if (used[i] == 0){//说明没被使用
used[i] = 1;
if (!search(tar - (i + 1), used)){//如果不成功
has.insert(make_pair(value, true));
used[i] = 0;
return true;
}
used[i] = 0;
}
}
has.insert(make_pair(value, false));
}
iter = has.find(value);//(B)
return (*iter).second;
}
当时以为如果for循环没有找到的话,那么在当时value值的状态为假!。那么如果为假的话,我们在if循环之后就可以直接返回false值,其实是一样的,为什么
算例(10,40)的时候出现问题?????至今还没明白,求高人指点!!!!!!