论文:天津大学的周伟的《状态压缩》

上述论文在这里有部分参考代码:状态压缩递推(States Compressing Recursion,SCR)

题目及解题思路可以在这里找到: (原创)BUCToj 动态规划一状态压缩 Problem E-I 解题报告之五连发

从以下两篇博文中挑选了练习题目,并参考了题目的翻译……

状态压缩DP总结【POJ3254】【POJ1185】【POJ3311】【HDU3001】【POJ2288】【ZOJ4257】【POJ2411】【HDU3681】 - 我叫MK - 博客频道 - CSDN.NET
http://blog.csdn.net/accry/article/details/6607703

状态压缩DP 题目小节 (一) - ddyyxx的程序员之路 - 博客频道 - CSDN.NET
http://blog.csdn.net/dyx404514/article/details/8754537

常用位运算:

a |= 1<<bit  //置位
a &= ~(1<<bit)  //清位
(a & 1 << bit) != 0  //测位方法1
(a >> bit & 1) != 0    //测位方法2

常用函数

//测一个数的二进制表示中是否有间隔小于3的1
bool Ok (int x)
{if (x&(x<<1))return false;if (x&(x<<2))return false;return true;
}//计算一个整型数x的二进制中1的个数
int Cal (int x)
{   int cnt=0;while (x){cnt++;x&=(x-1);}return cnt;
}//取所有子集
void Deal ()
{x=a;while (x)x = (x-1) & a;
}

以下我看懂的第一份状态压缩的代码……

题目链接:http://coder.buct.edu.cn/JudgeOnline/problem.php?cid=1032&pid=4

代码修改自:http://blog.csdn.net/zhang360896270/article/details/6596916

#include <cstdio>
#include <cstring>long long a[1100000];int main ()
{long long n;while (~scanf("%d",&n)){memset(a,0,sizeof(a));a[0]=1;for (int i=1; i<=1<<n ;i++){//注意这里是1左移n位不是n<<1,显然这里是在枚举0000~1111的每一种状态for (int j=i;j>0; j -= (j&-j)){//注意这里是倒推,因为要由之前的状态推出现在的状态a[i] += a[i&~(j&-j)];//这里的位运算处理甚是漂亮,它保证每一次都刚好取到i的子集//首先j&-j可以得出在i之前的每一种状态j的最低位1的位置k//然后取反可以保证只有第k个位置刚好为0,那么求与之后就在原来i的基础上去除了第k个1//比如说当前i枚举到0111,那么j&-j = 0001,则~(j&-j) = 1110,那么i&1110 = 0110,0110就是0111的一个子集//随后去掉当前最低位k,j变成0110,以此反复运算,直到j=0000}    }printf("%lld\n",a[(1<<n)-1]);}  return 0;
}

SGU 222

和上面那道差不多,用组合做更好些

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;const int N=1<<10;
int pre[N][N];
int sum[N];  //sum[i]表示十进制数i转成二进制后1的个数
int dp[11][N];
int n,k;int main ()
{int i,j;scanf("%d%d",&n,&k);if (k>n){printf("0\n");return 0;}sum[0]=0;int all=1<<n;for (i=1;i<all;i++)    //预处理sum[i]=sum[i>>1]+i%2;for (i=0;i<all;i++)for (j=0;j<all;j++)if (sum[j]>sum[i] && sum[j^i]==1)  //sum[j]>sum[i]保证状态j一定在i之后,sum[j^i]==1保证i为j的子状态pre[j][++pre[j][0]]=i;  //pre[j][0]记录状态j共有多少个前状态,pre[j][]记录前状态都有哪些dp[0][0]=1;int t,ans=0;for (i=1;i<=n;i++)for (j=0;j<all;j++){if (sum[j]>i)continue;for (t=1;t<=pre[j][0];t++)dp[i][j] += dp[i-1][pre[j][t]];if (sum[j]<i)  //等价于sum[j]+1<=i,表示如果在这行不放棋子 dp[i][j] += dp[i-1][j];if (i==n && sum[j]==k)   //到达第n行且放置棋子数达到kans += dp[i][j];}printf("%d\n",ans);return 0;
}

Poj 3254 Corn Fields

题意:一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,要求两个相邻的方格不能同时放牛,即牛与牛不能相邻。问有多少种放牛方案(一头牛都不放也是一种方案)

思路:使用了滚动数组

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#define mod 100000000
using namespace std;vector<int> t;   //保存每一行所有可能的放法int dp[2][400];
int can[13];bool check (int x)   //测连续两个1
{if (((x>>1)&x)==0)return true;return false;
}void Init ()
{memset(dp,0,sizeof(dp));t.push_back(0);for (int i=1;i<(1<<12);i++)if (check(i))t.push_back(i);
}int main ()
{Init();int n,m,i,j;scanf("%d%d",&n,&m);for (i=1;i<=n;i++){int temp=0,x;for (j=1;j<=m;j++){scanf("%d",&x);temp=temp*2+x;}can[i]=temp;   //能放的地方}int limit=1<<m,len=t.size();__int64 ans=0;for (i=0;i<len;i++)   //初始化第一行{if (t[i]>=limit)break;int now=t[i];if ((now|can[1])==can[1])  //该状态合法dp[1][i]=1;}for (i=2;i<=n;i++){int k=i&1;memset(dp[k],0,sizeof(dp[k]));    //记得清空滚动数组for (j=0;j<len;j++){if (t[j]>=limit)break;int now=t[j],s;if ((now|can[i])==can[i])for (s=0;s<len;s++){if (t[s]>=limit)break;int pre=t[s];if ((can[i-1]|pre)==can[i-1] && (pre&now)==0) //(pre&now)==0表示上一行不与本行相邻dp[k][j]=(dp[k][j]+dp[k^1][s])%mod;}}}for (i=0;i<len;i++){if (t[i]>=limit)break;ans=(ans+dp[n&1][i])%mod;}printf("%I64d\n",ans);return 0;
}

Poj 1185 炮兵阵地

思路:dp[i][j][k]表示第i行状态为j,第i-1行状态为k时的方案数。

状态转移方程:dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][t]+num[j]);     其中num[j]是j的二进制中1的个数

#include <cstdio>
#include <cstring>
#define max(a,b) ((a)>(b)?(a):(b))int n,m;
char map[110][20],num[110],top;
int stk[70],can[110];
int dp[110][70][70];bool Ok (int x)
{if (x&(x<<1))return false;if (x&(x<<2))return false;return true;
}//计算一个整型数x的二进制中1的个数
int Cal (int x)
{   int cnt=0;while (x){cnt++;x&=(x-1);}return cnt;
}//找到所有可能的合法状态,最多60种
void Init ()
{top=0;int total=1<<m;for (int i=0;i<total;i++)if (Ok(i)){stk[++top]=i;num[top]=Cal(stk[top]);}memset(dp,0,sizeof(dp));memset(can,0,sizeof(can));
}int main ()
{while (~scanf("%d%d",&n,&m)){Init ();int i,j,k,t;for (i=1;i<=n;i++)scanf("%s",map[i]+1);for (i=1;i<=n;i++){can[i]=0;for (j=1;j<=m;j++)if (map[i][j]=='H')can[i]+=(1<<(j-1));  //不能放的地方置1}for (i=1;i<=top;i++)if (stk[i]&can[1])continue;elsedp[1][i][1]=num[i];for (i=2;i<=n;i++)for (j=1;j<=top;j++){if (stk[j]&can[i])continue;for (k=1;k<=top;k++){if (stk[j]&stk[k])continue;for (t=1;t<=top;t++){if (stk[j]&stk[t])continue;dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][t]+num[j]);}}}int ans=0;for (i=1;i<=n;i++)for (j=1;j<=top;j++)for (k=1;k<=top;k++)ans = max(ans,dp[i][j][k]);printf("%d\n",ans);}return 0;
}

状态压缩dp学习小记part1相关推荐

  1. 状态压缩dp学习小记part2

    继续学习状态压缩的相关知识. 本来准备继续按照上篇博文里提到的那篇论文继续学习,但被矩形完全覆盖虐了回来,决定先做些其他的题增进理解之后再回来做. Zoj 3471 Most Powerful 题目链 ...

  2. 0x56. 动态规划 - 状态压缩DP(习题详解 × 7)

    目录 Problem A. 最短Hamilton路径 ProblemB. 蒙德里安的梦想 Problem C. Corn Fields Problem D. 小国王 Problem E. 炮兵阵地 P ...

  3. LeetCode 2044. 统计按位或能得到最大值的子集数目(状态压缩DP)

    文章目录 1. 题目 2. 解题 1. 题目 给你一个整数数组 nums ,请你找出 nums 子集 按位或 可能得到的 最大值 ,并返回按位或能得到最大值的 不同非空子集的数目 . 如果数组 a 可 ...

  4. LeetCode 1799. N 次操作后的最大分数和(回溯 / 状态压缩DP)

    文章目录 1. 题目 2. 解题 2.1 错误解 2.2 回溯超时解 2.3 回溯通过 2.4 状态压缩DP 1. 题目 给你 nums ,它是一个大小为 2 * n 的正整数数组. 你必须对这个数组 ...

  5. LeetCode 1723. 完成所有工作的最短时间(DFS+剪枝 / 状态压缩DP)

    文章目录 1. 题目 2. 解题 2.1 DFS 2.2 状态压缩DP 265 / 3871, 前6.85% 前3题题解: LeetCode 5649. 解码异或后的数组(位运算) LeetCode ...

  6. LeetCode 1066. 校园自行车分配 II(状态压缩DP)

    文章目录 1. 题目 2. 解题 2.1 回溯超时 2.2 状态压缩DP 1. 题目 在由 2D 网格表示的校园里有 n 位工人(worker)和 m 辆自行车(bike),n <= m.所有工 ...

  7. Victor and World(spfa+状态压缩dp)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5418 Victor and World Time Limit: 4000/2000 MS (Java/ ...

  8. 由NP完全问题引出动态规划——状态压缩DP

    " 所有部分都应当在非强制的情况下组合回一起.要记住,你重组的那部分原来就是你拆解的.因此,如果你不能让它们组合回来的话,那一定是有原因的.要想尽一切办法,除了用锤头." – IB ...

  9. 动态规划-状态压缩DP

    [SCOI2005] 互不侵犯 题目描述 https://www.luogu.com.cn/problem/P1896 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它 ...

  10. POJ 2411 Mondriaan‘s Dream(最清楚好懂的状压DP讲解)(连通性状态压缩DP)

    poj 2411 Mondriaan's Dream(最清晰的状压DP解析) 闫氏DP大法好 我们这里是一列一列地来,因为是一个棋盘性的状态压缩DP,从哪个方向都一样 摆放的小方格总方案数 等价于 横 ...

最新文章

  1. [转] android 中 pinyin4j的使用
  2. Zabbix服务端的安装及使用
  3. GPU Shader 程序调试方法
  4. web前端入门学习(纯干货)
  5. sqoop导入hive时间格式问题解决方案
  6. telnet协商过程--转载自arthurscfd的《telnet协商》
  7. djang常用查询SQL语句
  8. hadoop streaming 按字段排序与输出分割详解
  9. tp3.2 分析打印查询语句sql
  10. DateFormat与SimpleDateFormat区别和使用详解
  11. OpenStack HA集群1-Galera Cluster for Mysql
  12. Edge浏览器+百度翻译:识别图片类PDF中的文字并翻译
  13. 笔记本电脑无线Wifi热点设置工具
  14. Redis主从复制-Replication
  15. 【离散数学】第二章 笔记(完)
  16. 1.2RK3288积累
  17. Juniper SRX操作系统软件升级
  18. 《软件需求工程(第2版)》一1.5 需求工程定义
  19. JavaWeb仿twitter个人社交网络及微博服务网站
  20. IDEA选中一列数据

热门文章

  1. 已知主机IP地址,计算网络地址和广播地址
  2. 软件构造-线程安全性
  3. superpixels(超像素)
  4. 前端/后端、前台/后台的英文翻译选择
  5. MTK平台Camera驱动流程分析
  6. 【转】纯干货:PS高手完全自学宝典(原创文章)
  7. python做一个浏览器_用python做一个简单的浏览器
  8. java oracle 中文列_java oracle中文乱码怎么办
  9. [XPlane11/12]同步更新Zibo737插件下载-更新至3.54.17-插件搬运
  10. Win10系统安装打印机提示未安装打印机驱动程序,试图将驱动程序添加到存储区