状态压缩 - LeetCode #464 Can I Win
动态规划是一种top-down求解模式,关键在于分解和求解子问题,然后根据子问题的解不断向上递推,得出最终解
因此dp涉及到保存每个计算过的子问题的解,这样当遇到同样的子问题时就不用继续向下求解而直接可以得到结果。状态压缩就是用来保存子问题的解的,主要思想是把所有可能的状态(子问题)用一个数据结构(通常是整数)统一表示,再用map把每个状态和对应结果关联起来,这样每次求解子问题时先find一下,如果map里面已经有该状态的解就不用再求了;同样每次求解完一个状态的解后也要将其放入map中保存
状态压缩适用于二元状态,即每一列的取值只有0和1,且不适合求解规模很大的问题(否则状态太多根本保存不了)
LeetCode #464 Can I Win
https://leetcode.com/problems/can-i-win/
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.
一开始看到这题,我想这不就是博弈树嘛!然后也没多思考,就开始啪啦啪啦地敲miniMax,再搞个αβ剪枝感觉时间复杂度也差不多了。一提交结果TL,整个人都不好了,还以为自己剪枝没剪好,后来实在怂了看了discuss才意识到要用深搜,像博弈树那样等暴力广搜完早就超时了= =、(附上我超时的博弈树代码)
int minMaxTree( bool turn, set<int> &cho, int goal, int now, int a, int b )
{
if(now>=goal)
{
if(turn)
return -1;
else return 1;
}
set<int>::iterator itr;
if(turn)//max方
{
for(itr=cho.begin();itr!=cho.end();itr++)
{
set<int> tmp = cho;
tmp.erase(tmp.find(*itr));
a = minMaxTree(0,tmp,goal,now+*itr,a,b);
if(a>=b)
break;
}
}
else
{
for(itr=cho.begin();itr!=cho.end();itr++)
{
set<int> tmp = cho;
tmp.erase(tmp.find(*itr));
b = minMaxTree(1,tmp,goal,now+*itr,a,b);
if(a>=b)
break;
}
}
if(turn) return a;
else return b;
}
之后我便参考discuss上的代码实现了dp+状态压缩(基本上是照着打的...),思路是暴力深搜,用状态压缩记录重复的状态降低时间消耗。这里主要说一下状态的表示方法,该问题每个状态(子问题)之间的不同之处在于:
1. 可以选用的数
2. 当前目标数(当前离原始目标还差多少)
即当两个状态上述两项都相同时,则视为同一个状态。状态区分不需要考虑当前是哪一方,因为双方实质一样即都想赢
现在考虑如何表示状态,可选数上限为N,那么每个状态就会有N个布尔值表示对应的数是否已被使用,比如N=3,[true false true]表示1、3可以用,2已被用。用01表示的话就是101,可以发现其能够转换成对应的2进制数,因此用N位的2进制整数就可以直接表示每次的可选数情况
解决了如何表示可选用数,还需要表示当前目标,方法是直接把map放到vector里,用vector下标表示目标数,比如vector[goal][nums] = true,表示当目标数为goal,当前状态为nums时,玩家可以赢该游戏
bool miniMax( int status, vector<unordered_map<int,bool> > &dp, int goal, int maxn )
{
if(dp[goal-1].find(status)!=dp[goal-1].end())//该状态已经被搜索过
return dp[goal-1][status];
for( int i=maxn-1; i>=0; i-- )
{
if(status & (1<<i))//遍历每个数字,如果该数字还没被使用
{
//亦或,把该位变0表示使用该数字
if( i+1 >= goal || !miniMax(status^(1<<i),dp,goal-i-1,maxn) ) //如果当前已经能实现目标,或对方接下来不能赢
{
dp[goal-1][status] = true;
return true;
}
}
}
dp[goal-1][status] = false;
return false;
} bool canIWin(int maxChoosableInteger, int desiredTotal)
{
if(maxChoosableInteger>=desiredTotal)
return true;
if((maxChoosableInteger)*(maxChoosableInteger+1)/2<desiredTotal)//可选数之和小于目标则必定不可能成功
return false;
int status = (1 << maxChoosableInteger) - 1;//初始状态为全1即全部数字都可用
vector<unordered_map<int,bool> > dp(desiredTotal);//记录状态,dp[goal][sta]表示当前可用数为sta,目标为goal时能不能赢
return miniMax(status,dp,desiredTotal,maxChoosableInteger);
}
这里用unordered_map代替map,搜索时速度会更快(相应空间代价更高)。有个小插曲是在miniMax中传dp时忘记传引用,导致时传参因直接copy而不停地超时,且一直找不到原因= =、脑子不够用了
状态压缩 - LeetCode #464 Can I Win的更多相关文章
- [leetcode] 464. Can I Win (Medium)
原题链接 两个人依次从1~maxNum中选取数字(不可重复选取同一个),累和.当一方选取数字累和后结果大于等于给定的目标数字,则此人胜利. 题目给一个maxNum和targetNum,要求判断先手能否 ...
- [LeetCode] 464. Can I Win 我能赢吗
In the "100 game," two players take turns adding, to a running total, any integer from 1.. ...
- LeetCode 464. Can I Win
In the "100 game," two players take turns adding, to a running total, any integer from 1.. ...
- leetcode 864. 获取所有钥匙的最短路径(BFS,状态压缩)
题目链接 864. 获取所有钥匙的最短路径 题意 给定起点,要求在最短步骤内收集完所有钥匙,遇到每把锁之前只有 有对应的钥匙才能够打开 思路 BFS+状态压缩典型题目 先确定起点和总的钥匙数目,其次难 ...
- Number Game_状态压缩
Description Christine and Matt are playing an exciting game they just invented: the Number Game. The ...
- hdu 4336 Card Collector(期望 dp 状态压缩)
Problem Description In your childhood, people in the famous novel Water Margin, you will win an amaz ...
- hdu4336 Card Collector 状态压缩dp
Card Collector Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- hdu 5724 SG+状态压缩
Chess Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submi ...
- POJ-1143(状态压缩)
Number Game Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 3432 Accepted: 1399 Descripti ...
随机推荐
- layer.msg 添加在Ajax之前 显示进度条。
一.使用方法:1)必须先引入jQuery1.8或以上版本 <script src="jQuery的路径"></script> <script src= ...
- CSS3动画 transition和animation的用法和区别
transition和animation都是CSS3新增的特性,使用时需要加内核 浏览器 内核名称 W3C IE -ms- Chrome/Safari -webkit- Firefoc - ...
- daterangepicker 使用方法以及各种小bug修复
双日历时间段选择插件 — daterangepicker是bootstrap框架后期的一个时间控件,可以设定多个时间段选项,也可以自定义时间段,由用户自己选择起始时间和终止时间,时间段的最大跨度可以在 ...
- 单源最短路径问题-Dijkstra算法
同样是层序遍历,在每次迭代中挑出最小的设置为已知 ===================================== 2017年9月18日10:00:03 dijkstra并不是完全的层序遍历 ...
- LINUX 笔记-rm命令
常用参数: -f :就是force的意思,忽略不存在的文件,不会出现警告消息 -i :互动模式,在删除前会询问用户是否操作 -r :递归删除,最常用于目录删除,它是一个非常危险的参数
- [ACdream]女神教你字符串——违和感
题目描述: 女神最喜欢字符串了,字符串神马的最有爱了. 女神是一个重度强迫症患者,面对不是对称的东西,她会觉得太违和了,就会爆炸.所以她手上的字符串都是回文的,像什么a,b,aabaa,abcba,上 ...
- 如何通过C#操作Access,本人亲测通过
1. c# 操作access数据库 // it's your DB file path: // ApplicationEXEPath\Test.mdb var DBPath = "d:\\T ...
- h5样式初始化
nav, header, section, article, aside, footer { display: block; } body, p, pre, hr, ul, dl, dd, h1, h ...
- sqoop1.9.7安装和使用
安装1.下载sqoop1.9.7.地址: http://www.apache.org/dyn/closer.lua/sqoop/1.99.72.解压sqoop ,并配置环境变量 ~/.bash_pro ...
- 扩展 lua require 的行为
扩展 lua require 的行为 来源 https://blog.codingnow.com/2015/10/lua_require_env.html 今天同事提了个需求,他希望可以给部分 lua ...