topcoder-srm701-div2-900 博弈\计算二进制位1的个数\dp\状态压缩
借用一下qls翻译过来的题面
给定 n 和 m,问谁赢
n<=5e8, m<=50
TL 2s

如图所示,一个点是p-position的条件是,他的前驱节点是必胜态
一个点是N-position则要求他的后继节点全部都是必败态(否则他是做不到必胜,记住。。是必胜)
那么我们可以得到,标记一个点的规则是,观察当前未被标记的点的所有后继节点,如果它的后继节点全部都
达到了必胜态,则这个点一定是必败态,我们就标记成必败态,如果它的后继节点中至少有一个点是必败态,那么
当前的这个点一定是必胜态,因为如果有必胜的方法,只要有,我们就一定会去选择他。
了解了这个之后就差不多了。。
首先。。一开始有n个石子,但是我们每个人都不会清楚当前如何决策是最优的,因为从n个石子的状态开始,
没有一个点的后继节点的状态是明确的,我们要至少找出一个具有明确状态的点才能继续我们的工作
这时候我们需要做的工作是,找到这个博弈情景中的终态,什么是终态呢。。第一,已经分出胜负,
第二,从这个状态开始再也无法移动,游戏无法进行下去了。
那么这个题,有两个终态,第一当你面对石子的时候,它已经剩下了0个,游戏结束,你无法做任何操作,并且你输掉了比赛
第二,当你面对这堆石子时,他的数量写成二进制的形式后有奇数个1,而题目中说了,如果当前移动后的结果是奇数个1,那么
移动的这个人就会输,那么换句话说,当你正好面对这奇数个1时,不用做任何操作,因为上一个人已经输了(按照题目中的游戏定义)
所以我们至少要按照定义找到至少一个有明确状态的点,当我们从n个石子的这个状态出发去考虑,我们发现如果它的儿子们没有明确的
状态,那我就只能去找它儿子的儿子们,那么我们发现这其实是一个递归地过程,最终我们会找到叶子节点(有明确状态的点)
然后根据前面介绍的博弈论的基础知识去回溯,因为我们知道所有的后继节点都有了明确的状态之后,那么我们可以很容易的推知
他们的父亲节点是什么状态
int dfs(int n,int m,int dep){ if(ans[n]!=-) {
return ans[n];
}
if(n==){
return ans[n]=;//终态为零时输掉
}
if(Count(n)%==){
return ans[n]=;//当前二进制位数为奇数
}
register int i;
int res=dfs(n-,m,dep+);
for(i=;i<=m;++i){
if(n-i>=){
res&=dfs(n-i,m,dep+);
}
}
if(res==){
return ans[n]=;
}
else{
return ans[n]=;
}
}
以上为递归程序的版本
我们清楚了这个过程之后,发现,一开始的递归找叶子节点(有明确状态的点)这一过程是多余的
因为我们知道应用何种知识去找出叶子节点,即终态的意义明确
所以我们可以从递归树的底部,直接回溯上去
void solve(int n,int m){
int i,j;
int res=;
int dp[m];
for(i=;i<=m;++i){
dp[i]=;
}
for(i=;i<=n;++i){
// cur=res; for(j=;j<=m;++j){
if(n-j>){
res&=dp[j];
}
}
if(res==) {
res=;
}
else{
res=;
}
if(Count(i)%==){
res=;
}
for(j=m;j>=;++j){
dp[j]=dp[j-];
}
dp[]=res;
}
}
这个是递推程序的版本
为什么要开m大小的dp数组呢,这是因为,每个博弈的阶段,我们都有m个选择,选择1~m中的一个数,取出对应数的石子
能不能取,我们再说,到时候程序里判断一下就完事了
并且还有另外一个分析的方法,因为你是从递归树的底部往上推,那么你就需要知道上面那个点的所有子节点都是些什么
或者说如何找到它的所有子节点,那么我们知道因为你每个阶段最多作出m个决策(必胜必败,石子数不够的情况都会少于m个决策)
所以通过m我们就能找到它所有的后继节点的状态,因为每次都只用到了这m个状态,所以我们只存这m个状态,每次迭代即可
如何迭代呢?我们就要去分析一下递归树,每次这m个状态都是哪些状态(我都用到了哪m个状态),这个题呢,正好就是有规律的,不断用算出来的一直迭代
旧的状态就行,一定要自己画一画。
走到这一步。。我们注意到,我的dp数组里存的数,不是零就是1,而且m还比较小,最大50,那么long long是能存下的,这里有一个坑
那就是ll p=(1<<50);会溢出。。因为1默认是整型,必须要写成ll p=(1ll<<50)
所以我们考虑用long long来压m位,进行状态压缩。。,那么我怎么考虑我这m个状态的更新呢。。不用担心。。我们使用位运算
使用左移运算就能达到后面的位保存前面的位的效果。。但是这样的话,这个数就越来越大了,不是m位了。。会爆炸的。。所以
我们用一个m位全一的数跟压位的这个数做一下与运算,为的是这个m位全1的数的高位的零把压位的这个数的超过m位的不为1的位都截掉
然后最后一位用或运算更新状态即可。。这样就代替了上面的dp数组
void test(int n,int m){
ll allwin=(1ll<<m)-;
ll nowstate=allwin;
register int i,j;
for(i=;i<=n;++i){
if(Count(i)&||nowstate!=allwin){
res=;
}
else{
res=;
}
nowstate=((nowstate<<)&allwin)|res;
}
}
这里这个Count(int v)函数是用来计算一个数的二进制1的个数,显然这个时间复杂度是nlogn的
对于题目中的数据,这样铁定超时。。
所以说我们就要加速对于Count(n)的计算
然后呢。。我们注意到题目中n最大不会超过int,那么int一共有32位,我们可以用分治的思想
把它分成两半,那么一半就是1<<16了。。,于是乎。。我们就预处理出0~(1<<16)-1(16个1)这些数每个数有多少个1就行了
((pre[i>>16]+pre[i&((1<<16)-1)])&1)我们来理解一下,这个式子。。
前半句是说,取i的高16位,显然是在针对超过16位的i,如果i本来就不够16位,那么我们知道pre[0]=0;
那么后半句就是在说,把i的低十六位取出来。。
然后我们就能快速地预处理这个值了
我们还用到了一个__builtin_popcount,(注意前面有俩下划线。。。),这个是内置函数。。不需要头文件,这个函数算的还是贼快的。。
他能快速算出一个数有多少个二进制位的1
另外:string前面要加上std::
下面贴上代码
#include <iostream>
#include <cstdio>
#include <string.h>
#include <time.h>
#define ll long long
class ThueMorseGame{
public:
int pre[<<];
std::string get(int n,int m){
int i;
int res=;
ll allwin=(1ll<<m)-;
ll nowstate=allwin;
for(i=;i<(<<);++i){
pre[i]=__builtin_popcount(i);
}
for(i=;i<=n;++i){
if(((pre[i>>]+pre[i&((<<)-)])&)||nowstate!=allwin){
res=;
}
else{
res=;
}
nowstate=((nowstate<<)&allwin)|res;
}
//i 从1开始,nowstate全是1,是不对的,因为有一个0的必败态
//所以从零开始,初始化一下
return (nowstate&)?"Alice":"Bob";
}
};
int main(){
int st,ed;
int n,m;
scanf("%d%d",&n,&m);
ThueMorseGame p;
st=clock();
std::string ans=p.get(n,m);
ed=clock();
std::cout<<ans<<" time:"<<(double)(ed-st)/CLOCKS_PER_SEC<<"s"<<std::endl;
return ;
}
topcoder-srm701-div2-900 博弈\计算二进制位1的个数\dp\状态压缩的更多相关文章
- HDU 5724 Chess (状态压缩sg函数博弈) 2016杭电多校联合第一场
题目:传送门. 题意:有n行,每行最多20个棋子,对于一个棋子来说,如果他右面没有棋子,可以移动到他右面:如果有棋子,就跳过这些棋子移动到后面的空格,不能移动的人输. 题解:状态压缩博弈,对于一行2^ ...
- 神秘常量复出!用0x077CB531计算末尾0的个数 -- De Bruijn 序列
http://www.matrix67.com/blog/archives/3985 神秘常量复出!用0x077CB531计算末尾0的个数 大家或许还记得 Quake III 里面的一段有如天书般的代 ...
- [原创]java WEB学习笔记41:简单标签之带属性的自定义标签(输出指定文件,计算并输出两个数的最大值 demo)
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- Topcoder SRM 618 Div2 --900
题意:给定两个NxN的棋盘,每个棋盘都有一个‘车’的摆放状态,问进行若干次交换,能否使棋盘1变为棋盘2. 交换规则:每次选两个‘车’,坐标分别为(r1,c1),(r2,c2),如果r1<r2并且 ...
- ACM学习历程—TopCoder SRM691 Div2
这是我的第一次打TC,感觉打的一般般吧.不过TC的题目确实挺有意思的. 由于是用客户端打的,所以就不发题目地址了. 300分的题: 这题大意是有一段序列只包含+和数字0~9. 一段序列的操作是,从头扫 ...
- CF 299 div2 C 博弈
http://codeforces.com/contest/299/problem/C 题目大意: 给两个0,1串保证长度都是2*n(偶数),问,Yaroslav, Andrey按照顺序取,首先是ya ...
- TopCoder SRM596 DIV2 1000: SparseFactorialDiv2
题意: For an integer n, let F(n) = (n - 0^2) * (n - 1^2) * (n - 2^2) * (n - 3^2) * ... * (n - k^2), wh ...
- topcoder 594 DIV2 foxandclassroom
暴力枚举 1 #include <iostream> #include <vector> #include <string> using namespace std ...
- topcoder 643 DIV2
太弱了,太弱了! A:基本的判断吧,然后就是边界问题,写了好久,结果发现时房间第二个交的.. B:真心跪了,还好想出来了,思路想的太慢太慢,结果交上去,落后太多,不过HACK时很多人挂了, 这也是DI ...
随机推荐
- c#之Redis实践list,hashtable
写在前面 最近公司搞了一个活动,用到了redis的队列,就研究了下redis的相关内容.也顺手做了个demo. C#之使用Redis 可以通过Nuget安装Reidis的相关程序集.安装之后发现会引入 ...
- unity StreamingAssets路径
原地址:http://blog.csdn.net/nateyang/article/details/8493791 我们在读写例如XML和TXT文件的时候,在电脑上和手机上路径不一致,造成了很多麻烦, ...
- MyEclipse安装JS代码提示(Spket插件)
近期需要大量使用JS来开发,但是MyEclipse2014自带的JS编辑器没有代码提示的功能,开发效率有点低,所以安装了一个Spket的插件,过程非常简单,SVN插件的安装比这个更简单. Spket插 ...
- 2015安徽省赛 C.LU的困惑
题目描述 Master LU 非常喜欢数学,现在有个问题:在二维空间上一共有n个点,LU每连接两个点,就会确定一条直线,对应有一个斜率.现在LU把平面内所有点中任意两点连线,得到的斜率放入一个集合中( ...
- FOJ 2161 Jason and Number
暴力模拟找规律: 552287 2014-04-23 21:08:48 Accepted 2161 Visual C++ 0 ms 192KB 347B Watermelon #include< ...
- 重新编译安装gcc-4.1.2(gcc版本降级)之TFS安装
wget http://gcc.parentingamerica.com/releases/gcc-4.1.2/gcc-4.1.2.tar.gz tar -zxfv gcc-4.1.2.tar.gz ...
- macbook air 安装win7双系统
转自: http://jingyan.baidu.com/article/6d704a13f99f1a28da51ca49.html 1)遇到“No bootable device-insert bo ...
- 【云计算】Kubernetes、Marathon等框架需要解决什么样的问题?
闲谈Kubernetes 的主要特性和经验分享 Capitalonline全球云主机.全球私有网络,免费试用进行时 » 主要介绍 Kubernetes 的主要特性和一些经验.先从整体上 ...
- MVC3和MVC4相关问题
从TFS获取的MVC项目生成一直提示这个错误,我的VS中MVC3和MVC4都有,TFS项目中的MVC版本应该是MVC3的,现在我要用这个项目,但是一直是这个错误,请高手指点,积分不多了,见谅 程序集“ ...
- EtherCAT数据帧结构
EtherCAT数据直接使用以太网数据帧(以太网帧解释http://blog.chinaunix.net/uid-23080322-id-118440.html)传输,使用的帧类型为0x88A4.Et ...