状态压缩dp学习小记part1
2013-05-31 21:53
417 查看
论文:天津大学的周伟的《状态压缩》
上述论文在这里有部分参考代码:状态压缩递推(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
常用位运算:
常用函数
以下我看懂的第一份状态压缩的代码……
题目链接:http://coder.buct.edu.cn/JudgeOnline/problem.php?cid=1032&pid=4
代码修改自:http://blog.csdn.net/zhang360896270/article/details/6596916
SGU 222
和上面那道差不多,用组合做更好些
Poj 3254 Corn Fields
题意:一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用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的个数
上述论文在这里有部分参考代码:状态压缩递推(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 ; int sum ; //sum[i]表示十进制数i转成二进制后1的个数 int dp[11] ; 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行且放置棋子数达到k ans += 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; else dp[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------学习小记
- 状态压缩dp学习小记part2
- 插头与轮廓线与基于连通性状态压缩的dp 学习指南
- 【算法学习笔记】62.状态压缩 DP SJTU OJ 1088 邮递员小F
- 从hihoCoder的一道题学习状态压缩+dp
- 状态压缩dp学习 + poj3254(最简单的状态压缩dp)
- 学习笔记:状态压缩DP
- 树形DP学习小记Part1 选课 Hdu 1561 The more, The Better
- 学习笔记:状态压缩DP
- 【算法学习笔记】67.状态压缩 DP SJTU OJ 1383 畅畅的牙签袋
- ACM学习历程—HDU1584 蜘蛛牌(动态规划 && 状态压缩 || 区间DP)
- 状态压缩DP 不断学习中。。。。。。
- 状态压缩 DP 学习笔记
- 状态压缩动态规划(压缩状态DP)学习笔记
- ACM学习历程—ZOJ3471 Most Powerful(dp && 状态压缩 && 记忆化搜索 && 位运算)
- 铺砖问题 (状态压缩DP)
- HDU 3001 Travelling(状态压缩DP)
- ZOJ 3471 【状态压缩DP】
- ZOJ3070-The Colored Stones - 状态压缩dp
- POJ 2836 Rectangular Covering 状态压缩DP 几何