总结:
之前对状压只是一知半解的,今天认真的把学了一遍。
个人发现:状压DP和基础DP的区别并不大,状压只是巧妙的利用了很多位运算,本质和普通DP没什么区别。
换句话说,状压DP只是在状态的表示上利用了2进制,在判断一些状态的时候利用了位运算。
接下里同样是要想如何状态转移方程。感觉状压熟悉之后,利用二进制并不难,难的还是如何推出题目子结构性质和转移方程怎么来。
由于状压有不少位运算,所以强烈建议,对位运算还不熟练的同学先学一下位运算。
比如 x&(x<<1) 就是判断x转化为二进制之后,有没有相邻的1;x&=(x-1)可以把x的最右边的第一个1去掉;
x^(1<<(j-1)) 可以把x的右数第j为取反等等。
简单解释一下:
1) x<<1 把x左移以为,x&(x<<1) 即将x与它左移一位之后的二进制数 逐位进行 “与”操作
如果有相邻的1 1,它左移一位之后,肯定会出现 1&1的情况,那么x&(x<<1)的结果就不会是0.
2) x&=(x-1) 假设k=x-1;如果x(二进制形式)的最后一位是1,那k与x的差别就在于一个1一个是0,再逐位相与,0&1=0,所以最右边的这个1就被去掉了,如果x的最后以为是0,x-1,这个0必然要向高位借一个1,如果它最近的高位还是0,继续向前借,直到找到一个1,比如11000-1 = 10111;两者相与,11000&10011=10000。结果也是去掉了最右边的第一个1。
3) x^(1<<(j-1)) 1<<j 的结果显然是 10000... 即1后面j-1个0;异或的话可以这样理解:0异或一个东西等于它本身,1异或一个东西等于它取反;所以就把x的第右数j位取反了。
如果位运算也看不懂,几乎无从谈学状压。
两个入门的题目:
<span style="font-size:12px;">/* ***********************************************
Author :angon
************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define mst(a,k) memset(a,k,sizeof(a));
#define LL long long
#define maxn 1005
#define mod 100000000
/*
inline int read()
{
int s=0;
char ch=getchar();
for(; ch<'0'||ch>'9'; ch=getchar());
for(; ch>='0'&&ch<='9'; ch=getchar())s=s*10+ch-'0';
return s;
}
inline void print(int x)
{
if(!x)return;
print(x/10);
putchar(x%10+'0');
}
*/
int mp[13],st[1<<13],m,n;
int dp[13][1<<13];
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(~scann(n,m))
{
mst(mp,0);
mst(st,0);
mst(dp,0);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int x;scan(x);
if(!x) mp[i] += 1<<(m-j);
}
}
int k=0;
for(int i=0;i<(1<<m);i++) //预处理,把1不相邻的状态处理出来
{
if( (i&(i<<1)) == 0)
st[k++]=i;
}
for(int i=0;i<k;i++) //初始化第一行的状态
{
if( (mp[1]&st[i]) ==0)
dp[1][i]=1;
}
for(int i=2;i<=n;i++)
{
for(int j=0;j<k;j++) //枚举第i行的状态
{
if(mp[i]&st[j]) continue;
for(int f=0;f<k;f++) //枚举i-1行的状态
{
if(mp[i-1]&st[f]) continue;
if( (st[j]&st[f])==0) //如果竖着也不相邻
dp[i][j] += dp[i-1][f];
}
}
}
int ans=0;
for(int i=0;i<k;i++)
ans = (ans+dp[n][i])%mod;
printf("%d\n",ans);
}
return 0;
}</span><span style="font-size:14px;">
</span>
POJ 1185 炮兵阵地
/* ***********************************************
Author :angon
************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define mst(a,k) memset(a,k,sizeof(a));
#define LL long long
#define maxn 1005
#define mod 100000007
/*
inline int read()
{
int s=0;
char ch=getchar();
for(; ch<'0'||ch>'9'; ch=getchar());
for(; ch>='0'&&ch<='9'; ch=getchar())s=s*10+ch-'0';
return s;
}
inline void print(int x)
{
if(!x)return;
print(x/10);
putchar(x%10+'0');
}
*/
int mp[105],st[70],num[70],dp[105][70][70],m,n;
bool ok(int x)
{
if(x&(x<<1) || x&(x<<2))
return false;
return true;
}
int top;
void init()
{
top=0;
for(int i=0;i<(1<<m);i++)
if(ok(i))
st[++top]=i;
for(int i=1;i<=top;i++)
{
int cnt=0;
int t=st[i];
while(t)
{
cnt++;
t&=(t-1);
}
num[i]=cnt;
if(!(mp[1]&st[i]))
dp[1][1][i]=num[i]; //初始化第1行
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(~scann(n,m))
{
char s[11];
mst(mp,0);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=m;j++)
{
if(s[j]=='H')
mp[i] += 1<<(m-j);
}
}
mst(dp,-1);
init();
for(int i=1;i<=n;i++)
{
for(int t=1;t<=top;t++) //枚举第i行
{
if(mp[i]&st[t]) continue;
for(int j=1;j<=top;j++) //枚举第i-2行
{
if(st[t]&st[j]) continue;
for(int k=1;k<=top;k++) //枚举第i-1行
{
if(st[t]&st[k]) continue;
if(dp[i-1][j][k]==-1) continue; //如果上一个状态不存在
dp[i][k][t]=max(dp[i][k][t],dp[i-1][j][k]+num[t]);
}
}
}
}
int ans=0;
for(int k=1;k<=top;k++)
for(int t=1;t<=top;t++)
ans=max(ans,dp[n][k][t]);
printf("%d\n",ans);
}
return 0;
}