还是合并石子,但是这次可以任意两个合并,并且求最大异或和

f[s]表示把s所对应的的石子合并为一堆的最小代价

最后求f[2^n-1]

怎么转移?

最后一次也是把两堆合并成一堆,但是会有很多情况,可以枚举s的所有子集,把子集和剩下的部分合并

直接枚举比s小的数,看看是否s|a=s或s&a=a

复杂度O(4^n)

优化:如果自己直接枚举子集就会快一些

cin >> n;
for (int a=;a<n;a++)
cin >> z[a];
for (int a=;a<(<<n);a++)
for (int b=;b<n;b++)
if (a & (<<b))
sum[a] += z[b]; memset(f,0x3f,sizeof(f));
for (int a=;a<n;a++)
f[<<a] = ; /*for (int s=0;s<(1<<n);s++)
for (int a=1;a<s;a++)
if ( (s|a) == s)
f[s] = min(f[s],f[a]+f[a^s]+(sum[a]^sum[a^s]));*/ for (int s=;s<(<<n);s++)
for (int a=(s-)&s;a;a=(a-)&s)
f[s] = min(f[s],f[a]+f[a^s]+(sum[a]^sum[a^s])); cout << f[(<<n)-] << endl;

复杂度O(3^n)

注意枚举子集的方法(很重要)

博弈论DP

有一个游戏G,两个人玩(van♂),回合制,没有平局,胜负的区分方法是当某个人没办法进行操作的时候就输了

例:

有一个整数S(2<=S<=200),先手在S上减掉一个数x,至少是1,但小于S。之后双方轮流把S减掉一个正整数,但都不能超过先前一回合对方减掉的数的k倍,减到0的一方获胜。问先手是否必胜?

怎么dp?

一个游戏G有很多的状态,在一个状态下如果采用某种操作,可以把一个状态转移到另一个状态。如果没有状态可以转移了或者是转移到的都是必胜态,就终止了,这个状态叫做  必败态  意思是:谁在这个状态进行操作,谁就一定会输

对应的,能够转移到必败态的状态就是必胜态

f[s]代表游戏的某个状态必胜还是必败

如果存在f[s[[i]]=false,则有f[s]=true  如果对于所有的f[s[i]]=true,则f[s]=false

和什么有关?目前剩下的,队手上次减的。f[i][j]表示s当前还剩下i,对手上一次减的是j的情况必胜还是必败

枚举1<=r<=k*j,则可以转移到f[i-r][r]

用记忆化搜索

#include<iostream>

using namespace std;

bool f[][],g[][];

bool dfs(int i,int j)
{
if (i==) return false;
if (g[i][j]) return f[i][j];
g[i][j]=true;f[i][j]=false;
for (int r=;r<=i && r<=k*j;r++)
if (dfs(i-r,r) == false) f[i][j]=true;
return f[i][j];
} int main()
{
cin >> s >> k;
for (int a=;a<s;a++)
if (dfs(s-a,a) == false)
{
cout << "Alice" << endl;
return ;
}
cout << "Bob" << endl; return ;
}

现在有n个游戏,怎么搞?

例:取石子游戏

有n堆石子,每次可以从某一堆石子里面取走任意多的石子。当谁没有办法取石子时就输了。先手必胜还是必败?

假如现在只有一堆石子

SG函数

定义sg[0]=0  sg[i]!=0表示i是必胜态   sg[i]=0表示i是必败态

sg[1]:找到1能转移到的所有状态,把他们的sg函数都写出来,在找到最小的没有出现过的自然数

对于sg[n],找到最小的没有在sg[0],sg[1]...sg[n-1]中出现的自然数,这个数就是sg[n]的值

sg函数有什么用?

回到这个问题,我们发现sg函数只对一个游戏求,但是这个题是多个游戏,怎么把一个变成n个?

sg定理:n个游戏的sg值等于每个游戏的sg值异或起来

证明:拿头证

然后这个题经过手推发现sg[n]=n,这样直接搞就好了

int main()
{
cin >> n;
int ans=;
for (int a=;a<=n;a++)
{
int v;
cin >> v;
ans = ans ^v;
}
if (ans!=) cout << "Alice" << endl;
else cout << "Bob" << endl;
}

例:给你n堆石子,每一次可以在某一堆石子中取走1~4个石子,问先手必胜还是必败

列出来sg函数找找规律,发现sg[i]=i%5,然后抑或起来就好了

一般这种题就是自己手算出sg函数然后抑或起来就好了

例:n+1堆石子,最左边一堆石头有2012个,两个人分别进行操作。一次操作可以选取两堆不同的石堆分别增加或减少一个石子(一加一减,或给已经不剩石子的堆加一个都是允许的)。为了保证游戏会在有限步内结束,规定所选的两堆中右边的那一堆一定要包含奇数个石子,无路可走者输.问先手是否必胜?

这个题非常恶心的一点就是不同的堆有联系,所以考虑把他转化成最基本的问题

先看每一堆是奇数个还是偶数个,答案就是把所有奇数的下标取出来异或,看是否等于0

有N堆石子放在N级楼梯上,楼梯编号为0..N-1,每堆有a[n]个石子。两人轮流游戏,每次将任意堆中的任意个石子移动到它下面那一层楼梯上,0号的石子不能动。直到所有石子都移动到0号,那个人就赢了。问先手是否有必胜策略 。

把所有下标为奇数的石子数量异或起来

因为如果把偶数位置的石子扔到奇数上,那么我只需要把奇数的位置移到下一个偶数就好了

所以偶数位置上的石子对答案没有影响,且奇数位置上的石子搬到偶数上后就没有影响了

例:N*M的棋盘,每个格子有一定数量棋子,每次可将某个格子部分或全部棋子向右或向下移动,问先手必胜还是必败。

和上个题差不多,只需要把所有距离为奇数的点的距离异或起来就行了

因为如果从偶数移动到奇数,一定可以从奇数移动到偶数,反之则不然

例:1xN(1<=N<=2000)的空格子,双方轮流操作,每次选一个没有被标记的格子,将其标记,如果某人操作完后,存在3个连续的格子都被标记了,那么他就获胜了,问先手是否有必胜策略?

如果把他当成一个游戏,我们需要搞一个2000位的二进制数,存都存不下

考虑把他当成多个游戏

如果我们先手在一个位置打标记,后手就不能在这个位置左两个右两个打标记

状态sg[i]表示对于一个长度为i的横条它的sg是多少

Vector存所有能转移到的sg值

枚举染色点,把原来的横条分成两个小的横条,算出来左边和右边的长度

怎么计算mex?

先排序,从0,1,2,3这样一直看,直到找到一个没有的。为了避免数组越界,直接加一个很大的数就可以了

#include<iostream>
#include<algorithm>
#include<vector> using namespace std; int dfs(int n)
{
if (n==) return ;
if (f[n]) return sg[n];
f[n]=true;
vector<int> z;
for (int a=;a<=n;a++)
{
int l = max(a-,);
int r = max(n-a-,);
z.push_back( dfs(l) ^ dfs(r) );
}
sort(z.begin(),z.end());
z.push_back(); for (int a=,p=;;a++)
{
if (z[p] != a)
{
sg[n] = a;
return sg[n];
}
while (z[p]==a)
p++;
}
} int main()
{
cin >> n;
if (dfs(n)) cout << "Alice" << endl;
else cout << "Bob" << endl;
}

看看能不能有一种情况转移到必败态

但是开30000*30000的数组就炸了

怎么优化

把状态改成f[a][b][y],因为x只可能是2的倍数或者3的倍数

qbzt day6 上午的更多相关文章

  1. Day6上午解题

    预计分数:100+100+30=230 实际分数:90+25+10=125 T1少判了一种情况,T2的贪心是错的,T3被卡了... T1 模拟水题,注意20的可以用3个5块的找 #include< ...

  2. qbzt day7上午

    由于优盘咕咕咕了,所以这篇就咕咕咕了 以后还会补上的 qwq

  3. qbzt day6 下午 模拟赛

    我太菜了 T2 给定一张有向图,每个点有点权.试找到一条路径,使得该路径上的点权最 大值减去点权最小值最大,问这个差最大是多少.   话说这个题第一个想到的思路是tarjan缩点+拓扑排序来着... ...

  4. qbzt day5 上午

    动态规划 递推  递归   记忆化搜索 斐波那契数列 1.用其他已经计算好的结果计算自己的结果(递推) 2.用自己的值计算别人的值(考虑对之后的项做出的贡献) cin >> n; f[]= ...

  5. qbzt day4 上午

    图论 最短路:dijkstra   spfa   floyd 最小生成树:kruskal 连通性:bfs/dfs    tarjan(强连通分量) 其它:拓扑排序    LCA 齿轮: 图的dfs树只 ...

  6. qbzt day3 上午

    内容提要 堆 lca(最近公共祖先) st表 hash 并查集 树状数组 线段树 数据结构 1.堆 Priority_queue 他滋兹:插入删除查询最大值(最小值) 分为大根堆小根堆 2.LCA 首 ...

  7. qbzt day2 上午

    内容提要 贪心 分治 分块 搜索 接着昨天的讲 过河问题 考虑AB是最快的人,CD是最慢的人,要把CD两个人送过河,只有两种方案,牵扯到四个人,并且n个规模的原问题化成了n-2个规模的子问题 那么最后 ...

  8. qbzt day1 上午

    内容提要 模拟,贪心 在讲这些东西之前,我们先来了解一个东西:high level 这个东西大体上就是你做题之前要先想清楚自己要写什么,怎么写,然后再写,不要有一点写一点 1.模拟 模拟算法算是很水的 ...

  9. 2022寒假集训day6

    day6上午还是做四道题T1区域[上机练习]1.编程计算由"*"号围成的下列图形的面积.面积计算方法是统计*号所围成的闭合曲线中水平线和垂直线交点的数目.如下图所示,在 10*10 ...

随机推荐

  1. 通过实例简介python使用ctypes模块调用C语言动态库

    看介绍python语言时,说它是胶水语言,可以调用其他语言.通过使用ctypes模块就可以调用C语言的动态库.下面先放上官方文档和几个比较好的博文. 1.官方文档:http://python.net/ ...

  2. 通过编写串口助手工具学习MFC过程——(七)添加Tab Control控件

    通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...

  3. Elastic Search闪退问题

    昨天还可以正常启动,今天及不行.. 在网上找了很多方法都不行,后来参考https://blog.csdn.net/happyzxs/article/details/89156068,修复好了 一.遇到 ...

  4. 关于原型链,原来这么简单?—————终结__proto__和prototype的那些事

    今天,一个技术群里小朋友提出一个问题: Object.prototype.a = function () { console.log('a') } Function.prototype.b = fun ...

  5. R语言multiplot函数绘制多张图像

    必备数据包“Rmisc” 先准备好需要绘入的图像,以比如p1.p2表示, multiplot(p1, p2) 如果插入图像多,p1.p2.p3.p4,则需要规定图像排列顺序. multiplot(p1 ...

  6. Qt项目中main主函数及其作用

    http://c.biancheng.net/view/1821.html main.cpp 是实现 main() 函数的文件,下面是 main.cpp 文件的内容. #include "w ...

  7. caffe与tensorflow中的pooling

    两个框架对poolin的处理方式不同,这就导致在转模型时容易踩雷 tensorflow通过“VALID”和“SAME”参数来控制 caffe 通过pad值来控制 参考:https://blog.csd ...

  8. python3-多重继承

    继承是面向对象编程的一个重要的方式,因为通过继承,子类就可以扩展父类的功能. 回忆一下Animal类层次的设计,假设我们要实现以下4种动物: Dog - 狗狗: Bat - 蝙蝠: Parrot - ...

  9. 带加载进度的Web图片懒加载组件Lazyload

    在Web项目中,大量的图片应用会导致页面加载时间过长,浪费不必要的带宽成本,还会影响用户浏览体验. Lazyload 是一个文件大小仅4kb的图片懒加载组件(不依赖其它第三方库),组件会根据用户当前浏 ...

  10. CentOS6.5 安装gitlab以及gitolite迁移gitlab

    CentOS6.5 安装gitlab以及gitolite迁移gitlab gitlab 的安装使用以及数据结构 安装 环境: CentOS6.5 基于 nignx + unicorn 搭建的应用环境, ...