464. 我能赢吗
思路:
乍一看又像是脑筋急转弯的题目,但是看标签发现竟然可以用动态规划,这个我觉得很难理解,看题解里有一个用递归写的,我觉得思路不错,记录一下:
public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
int sum=0;
for(int i=1;i<=maxChoosableInteger;i++) sum+=i;
if(sum<desiredTotal) return false;//所有的数取完了仍然小于desiredTotal
Map<Integer,Boolean> map = new HashMap<>();
return canWin(maxChoosableInteger,desiredTotal,0,map);
}
public boolean canWin(int chooseable,int nowTarget,int used,Map<Integer,Boolean> map){
if(map.containsKey(used)) return map.get(used);//判断是否计算过(记忆化递归)
for(int i=1;i<=chooseable;i++)//我方选数
{
if((used & (1<<i)) == 0)//判断是否使用过该数(因为题目中说过maxChoosableInteger小于20《32,故可以用每一位代表该数是否被选用过)
{
if(nowTarget <= i || !canWin(chooseable,nowTarget-i,used | (1<<i),map))//一步即可赢||找到一步使对方无法赢
{
map.put(used,true);
return true;
}
}
}
map.put(used,false);
return false;
}
其主要代码部分就是记忆化递归,首先遍历所有的数作为起始数,为了防止重复遍历将所有数和对应结果保存在map中,以后遇到就不用再次递归而可以直接使用结果。
另外很重要的一点是,在递归的每一条路径上不可以使用重复的数字,为了记录此路径使用过的数字,他使用了一个比较巧妙的方法,因为题目中说过maxChoosableInteger小于20<32,故可以用每一位代表该数是否被选用过(之前某个题目也使用过类似的思路)
但其实最巧妙的是这一句:
if(nowTarget <= i || !canWin(chooseable,nowTarget-i,used | (1<<i),map))
通过这一句直接判断了对手的状态,实在是很巧妙~