状态压缩动态规划
动态规划的状态有时候比较难,不容易表示出来,需要用一些编码技术,把状态压缩的用简单的方式表示出来。典型方式:当需要表示一个集合有哪些元素时,往往利用2进制用一个整数表示。
*:一般有个数据 n<16 或者 n<32 这个很可能就是状态DP的标志,因为我们要用一个int的二进制来表示这些状态。要注意好这些数据规模的提示作用。
*:确定了为状态DP,那么第一步就是预处理,求出每行所有可能的状态了,cnt记录总的状态数,stk[]记录所有的可能状态。以炮兵阵地为例:
int cnt, stk[MAX];
void findStk(int n){ // 求出所有可能的状态。
for(int i = 0; i < (1<<n); i ++)
if(ok(i)){ // 判断这种状态可不可行。
stk[cnt] = i;
sum[cnt ++] = getSum(i); // 计算这种状态包含了几个炮兵。
}
}
bool ok(int x){ // 判断状态x是否符合,即是否会出现两个大炮间隔小于2。
if(x & (x<<1)) return false;
if(x & (x<<2)) return false;
return true;
}
int getSum(int x){ // 求出状态x中安装了多少门大炮,x的二进制有几个1。
int num = 0;
while(x > 0){
if(x & 1) num ++;
x >>= 1;
}
return num;
}
*:然后就是DP部分了,明确好状态转移方程。先特殊处理第1行,然后按状态转移方程求出剩下的值。
http://poj.org/problem?id=3254
#include <iostream> #include <string.h> #include <algorithm> using namespace std; const int mod=100000000; const int maxn=12; int dp[maxn+1][(1<<maxn)+1]; int num[maxn+1]; int n,m; bool check(int i,int x)//检查第i行出现状态x是否合法 { if((x&num[i])!=x)//很巧妙,判断第i行出现的状态x是否合法,为什么这么写,因为0的地方不能放牛, //合法状态与原始状态0位且为0,原始状态1位的地方与合法状态对应位且都等于合法状态位上的数字 return 0; if(x&(x>>1)||x&(x<<1))//不能有相邻的两个1 return 0; return 1; } int main() { cin>>n>>m; int x; memset(num,0,sizeof(num)); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { cin>>x; if(x) num[i]=num[i]|(1<<(j-1));//把每一行的状态保存到num[i]中去 } int Max=(1<<m); dp[0][0]=1;//注意这一句啊! for(int i=1;i<=n;i++)//枚举每一行 { for(int j=0;j<Max;j++) { if(!check(i,j)) continue; for(int k=0;k<Max;k++) if((j&k)==0) { dp[i][j]+=dp[i-1][k]; if(dp[i][j]>=mod) dp[i][j]%=mod; } } } int ans=0; for(int i=0;i<Max;i++) { ans+=dp[n][i]; if(ans>=mod) ans%=mod; } cout<<ans; return 0; }
http://acm.hdu.edu.cn/showproblem.php?pid=3001
http://acm.hdu.edu.cn/showproblem.php?pid=4568
POJ 3311
http://acm.fzu.edu.cn/problem.php?pid=2093
#include<cstdio> #include<string> #include<cstring> #include<vector> #include<iostream> #include<queue> #include<algorithm> using namespace std; typedef long long LL; const int INF = 0x7FFFFFFF; const int maxn = 1 << 16; int T, n, m, dp[maxn], f[maxn], x, y; queue<int> p; int main() { scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) f[i] = 0; for (int i = 1; i < (1 << n); i++) dp[i] = INF; while (m--) { scanf("%d%d", &x, &y); f[x - 1] |= 1 << (y - 1); f[y - 1] |= 1 << (x - 1); } //for(int i=0;i<n;i++) cout<<f[i]<<" ";cout<<endl; for (int i = 0; i < n; i++) if (!f[i]) f[i] = 1 << i; while (!p.empty()) p.pop(); p.push(0); while (!p.empty()) { int q = p.front(); p.pop(); int now = 0; for (int i = 0; i < n; i++) if ((q&f[i]) == f[i]) now |= 1 << i; for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (dp[now | (1 << i) | (1 << j)] > dp[q] + 1) { dp[now | (1 << i) | (1 << j)] = dp[q] + 1; p.push(now | (1 << i) | (1 << j)); } } } } int k = (1 << n) - 1, ans = dp[k]; //for(int i=0;i<n;i++) cout<<(k ^ (1 << i))<<dp[k ^ (1 << i)]<<" ";cout<<endl; for (int i = 0; i < n; i++) ans = min(ans, dp[k ^ (1 << i)]); if (ans == INF) printf("-1\n"); else printf("%d\n", ans); } return 0; }