赞
踩
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
做题做到状态压缩dp真的看不懂答案了,从b站找了两个课听,受益匪浅,做了笔记。
核心:从集合角度分析dp问题,经验也重要
有限级的最优化:从定义求:枚举太难
DP优化:1.化零为整(状态表示fi)a:集合
b:属性(min,max,count)
i代表对i件物体做决策,有两种方式—放入背包和不放入背包。
j表示当前背包剩余的容量。
分析:1.状态表示(每一维度):集合:只考虑前i个物品,总体积不超过j的选法的集合 属性:max f(n,v)
2.状态计算:选/不选第i个物品,分别取这俩的max
不选i:从1—i-1中选,且总体<=j 选:必然包含i(不变wi价值),f(i-1,j)。 前面随便(变化部分f(i-1,j-vi)。
左右两边取max
空间优化:用滚动数组,只保留之前结果,从大到小循环。可以优化成一维。
代码如下:
- #include<iostream>
- const int N=1010;
- using namespace std;
- int n,V;
- int f[N][N];
- int v[N],w[N];
-
- int main(){
- cin>>n>>V;//n:一共有多少物品,v背包容量
- for(int i=1;i<=n;i++)cin>>v[i]>>w[i];//背包第i个物品的容量和价值
-
- for(int i=1;i<=N;i++){
- for(int j=0;j<=V;j++){
- f[i][j]=f[i-1][j];
- if(j>=v[i])f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
- }
- }
- cout<<f[n][V]<<endl;
- return 0;
-
-
- }

动态规划随着阶段增长,在每个状态维度上会不断扩展;任意时刻已求解状态和尚未求解的状态在各个维度上的分界点组成DP扩展的轮廓。对于某些问题,我们需要在动态规划的“状态”中记录一个集合,保存这个“轮廓”的详细信息,以便进行状态转移。 若集合大小不超过N,集合中每个元素都是小于K的自然数,则我们可以把这个集合看作一个N位K进制数,以一个[ O , K N − 1 ] [O,K^{N-1}][O,K N−1]之间的十进制整数的形式作为DP状态的一维。
状态压缩DP:将集合转化为整数记录在DP状态中的一类算法。
使用条件:一般能够采用状态压缩dp的题目数据规模都不大(<30),这是判断是否能够采用状态压缩dp的一个重要条件。(以上从别人博客引用)
基础dp:背包,区间dp,树形dp
了解二进制运算符:<< >> | & ^
找哈密顿回路
二进制集合表示法:
状态表示
为什么不记录路径:因为要减少状态,不用去记录走过的路
转移方程
子集枚举
如何枚举这个集合?
1.推以后
- for(int s=0;s<(1<<n);++s)枚举s
- for(int i=0;i<n;i++)if(s&(1<<i))//枚举i,如果i节点在集合中
- for(int j=0;j<n;j++)//枚举下一步要去的节点
- If(!(s&(1<<j))&&G[i][j])//如果j不在且有路径
- dp[j][s|(1<<i)]=min(dp[j][s|(1<<j)],dp[i][s]+G[i][j])
2.推之前
- for(int s=0;s<(1<<n);++s)枚举s
- for(int i=0;i<n;i++)if(s&(1<<i))//枚举i,如果i节点在集合中
- for(int j=0;j<n;j++)//枚举之前的节点
- If((s&(1<<j))&&G[j][i])//如果j在,且有路径
- dp[i][s]=min( dp[i][s],dp[j][s^(1<<i)]+G[j][i])
s^(1<<i)//按位异或
s的第i个二进制位肯定是1,在j那里第i位是0,因为还没访问呢,所以要把i去掉
感谢b站的老师
一个是电子科大的,一个是闫老师讲的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。