动态规划 —— 状压DP (附一些位运算小知识)

状态压缩类动态规划

又叫集合动态规划。
参考学长博客和信息学奥赛一本通。

【基本概念】

通常将以一个集合内的元素信息作为状态且状态总数为指数级别的动态规划称为状态压缩动态规划。
其是一类以集合信息为状态的特殊的动态规划问题,主要有传统集合动态规划与基于连通性状态压缩的动态规划两种。
其原理是通过二进制位运算将状态压缩(用整数表示集合)作为动态规划的状态来解决问题。
通常具备以下两个特点:

  • 数据规模的某一维或几维特别小
  • 需要具备动态规划问题的两个基本性质:最优性原理、无后效性原则
【一些位运算的小知识】
  • 左移: << 相当于*2
  • 右移: >> 相当于/2
  • 与 : &
  • 或 : |
  • 非 : !
  • 计算当前数二进制最低位的1代表的值:int lowbit(int x){return x&(-x);}
  • 判断x二进制的第 i 位是否为0: int pd(int i,int x){return ((1<<(i-1))&x == 0);}
  • 将x二进制的第 i 位设置成1:int setb(int i,int x){return (x|(1<<(i-1)));}
  • 将x第二进制第 i 位设置成0:int setb(int i,int x){return (x&~(1<<(i-1)));}
  • 把x二进制下最靠右的第一个1去掉:x = x & (x-1)
  • 计算x二进制下含有1或0的个数:int num=0; while(x){x = x & (x-1); num++;}
  • 判断x是否为 2 的 n 次方:bool pd(int x){ return ((x & (x-1)) == 0);}
【经典例题——旅行商问题(TSP)】

问题: 一个 n (n<=15) 个点的带权完全图,求权和最小的经过每一个点恰好一次的封闭回路。
代码:

const int inf=0x3f3f3f3f;
const int maxn = 20;
int dp[(1<<n)][maxn],dis[maxn][maxn];
void slove()
{
    //n表示点的个数
    memset(dp,inf,sizeof(dp));
    dp[(1<<n)-1][0] = 0;//dp[s][v]表示从v出发,访问未访问的点的最短路径和,其中s表示已经访问点的集合
    for(int s=(1<<n)-2; s>=0; s--)
        for(int v=0; v<n; v++)
            for(int u=0; u<n; u++)
                if(!(s&(1<<u)))//u不在s中
                    dp[s][v]=min(dp[s][v],dp[s|(1<<u)][u]+dis[v][u]);
    cout << dp[0][0]) <<endl;
}
【使用】

在任意时刻,已经求出最优解的状态与尚未求出最优解的状态在各维度上的分界点组成了 DP 扩展的轮廓,对于某些问题,需要在动态规划的状态中记录一个集合,保存这个轮廓的详细信息,以便进行状态转移。
若集合大小不超过 n,集合中每个元素都是小于 k 的自然数,则可以把这个集合看作一个 n 位 k 进制数,以一个[0, k n k^{n} kn - 1 ]之间的十进制整数的形式作为 DP 状态的一维。

【例题】

//待整理

你可能感兴趣的:(动态规划,笔记)