状态压缩 - 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 ...
随机推荐
- vDSP加速的应用
vDSP 是IOS提供一系列加速处理算法..在优化时可以考虑应用一二... 1.在项目中加入Accelerate.framework库 点开项目属性->Build Phases->Link ...
- Lavarel artisan 命令
[alex@iZ25c5aeyiiZ yiqizou3.0]# php artisan list Laravel Framework version Usage: command [options] ...
- 突发小事件,USB接口问题
昨天遇到的突发事件,突然USB接口全部瘫了,键盘鼠标全部不能用,换到别人电脑上可以,吓尿了,以为本子主板挂了,但是发现插U盘竟然可以识别而且可以打开,感觉可能是静电问题,果然,彻底关机,拔掉电池,然后 ...
- PhiloGL学习(2)——骚年,让我们荡起双桨
前言 上一篇文章中简单介绍了PhiloGL框架如何上手.GLSL语言以及简单的绘制一个方块(见PhiloGL学习(1)--场景创建及二维方块加载).本文很简单,我们一起来让这个方块动起来. 一. ...
- 【原创】基于禅道的Bug管理操作规范
1. 禅道简介 禅道是一个基于"敏捷开发"模式的软件开发全生命周期管理软件,在国内的软件开发公司里占据了超过70%的份额,从大公司到小公司,都能适用. 禅道官网:http://ww ...
- Handler学习
刚开始学习Android的时候,知道异步线程无法更新UI,于是然后找了个东西把更新的动作抛给UI线程,这个东西就是Handler. 一开始就只会在主线程也就是UI线程new一个Handler,之后在各 ...
- virualbox 搭建 otter
前言 为了学习otter,上一篇我们讲到了 otter 必要软件的安装,参考:virualbox 安装 otter 必备软件,现在安装otter,相比官方文档,我们尽量简化安装步骤. virualbo ...
- [C#]使用ILMerge将源DLL合并到目标EXE(.NET4.6.2)
本文为原创文章,如转载,请在网页明显位置标明原文名称.作者及网址,谢谢! 本文主要是使用微软的ILMerge工具将源DLL合并到目标EXE,因此,需要下载以下工具: https://www.micro ...
- Abp异常-找不到方法:“System.String Abp.Runtime.Security.SimpleStringCipher.Decrypt(System.String, System.String, Byte[])”
解决方法:升级Abp.Zero版本到2.0.2
- mysql索引优化建议
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...