状态压缩优化动归
这个星期的任务就是它了,看了看周伟的论文,开始做题,做一道传一道吧。
【题目大意】给一个矩阵,可以放牧的点为1,不可以的为0,且放牧的时候任意2头牛不能相邻(包括上下和左右),问总共多少放牧方案
【解析】状态转移方程很好列,用f[i][j]表示第i行的状态为j的时候的方案数,j的二进制每一位表示一个格子0为不放,1为放。
很明显的有f[i][j]=∑f[i-1][k] k为与j不冲突的放法。 边界是f[1][j]=0(j合理)/1(j不合理)
刚学位运算,算是用到了一些技巧:
①判断是否相邻, 可以用k&(k<<1)
②存初始矩阵的时候可以反过来用1表示不可以,0表示可以,那么判断放法时候合理的时候就简单多了,见代码里的check2()
POJ 3245
1 /** 2 *For 状态压缩dp练习 3 *Prob : POJ 3254 4 *Data : 2012-6-14 5 *Author : ZhouHang 6 */ 7 8 #include <cstdio> 9 #include <cstring> 10 11 #define MaxM 20 12 #define MaxState 5000 13 #define oo 100000000 14 15 using namespace std; 16 17 int tot=0,m,n; 18 int state[MaxState],s[MaxM]; 19 int f[MaxM][MaxState]; 20 21 bool check(int k) 22 { 23 //验证相邻 24 if ( k&(k<<1) ) return false; 25 return true; 26 } 27 bool check2(int x,int k) 28 { 29 //s[k]中1是不能放牧 30 if (x&s[k]) return false; 31 else return true; 32 } 33 34 //f[i][j] 第i行为j的方法数 35 void Dp() 36 { 37 memset(f,0,sizeof(f)); 38 39 //预处理第一行 40 for (int i=1; i<=tot; i++) 41 if ( check2(state[i],1) ) 42 f[1][state[i]]=1; 43 44 for (int i=2; i<=m; i++) 45 for (int j=1; j<=tot; j++) 46 { 47 if (!check2(state[j],i)) continue; 48 for (int k=1; k<=tot; k++) 49 { 50 //if (!check2(state[k],i-1)) continue; 51 if (state[k]&state[j]) continue; 52 f[i][state[j]]+=(f[i-1][state[k]]%oo); 53 f[i][state[j]]%=oo; 54 } 55 } 56 } 57 58 int main() 59 { 60 61 scanf("%d%d",&m,&n); 62 63 int tmp; 64 for (int i=1; i<=m; i++) 65 { 66 s[i]=0; 67 for (int j=1; j<=n; j++) 68 { 69 scanf("%d",&tmp); 70 //反过来存,方便check2判断 71 if (tmp==0) s[i]+=1<<(n-j); 72 } 73 } 74 75 //Search all states 76 int limit=(1<<n)-1; 77 for (int i=0; i<=limit; i++) 78 if ( check(i) ) state[++tot]=i; 79 80 Dp(); 81 82 int ans=0; 83 for(int i=1; i<=tot; i++) 84 ans=(ans+f[m][state[i]])%oo; 85 86 printf("%d\n",ans); 87 88 89 return 0; 90 }
【题目大意】见原题
【解析】跟上面的那个类似,只不过这次每个状态要影响的就不是一行了,而是跟上行和上上行有关,于是我们的状态就要加一维了,用f[i][j][k]表示第i行状态下标是i,第j状态是k,原因是我们每次要枚举3行的状态,为了转移,必须记录两行以保证唯一,那么f[i][j][k]=max(f[i-1][k][t]+num[j]) num放的是每种状态的1的个数,因为空间只有64MB,所以我们要用到滚动数组
同样记录下位运算的技巧:可以用x&=(x-1)来去掉最靠后的一个1
POJ 1185
/** *For 状态压缩dp练习 *Prob : POJ 1185 *Data : 2012-6-14 *Author : ZhouHang */ #include <cstdio> #include <cstring> #include <algorithm> #define MaxState 610 #define MaxN 110 using namespace std; int tot=0,m,n,ans=0; int state[MaxState],s[MaxN],num[MaxState]; int f[MaxState][MaxState]; int tmp[MaxState][MaxState]; bool check(int k) { //验证相邻 if ( k&(k<<1) ) return false; if ( k&(k<<2) ) return false; return true; } bool check2(int x,int k) { //s[k]中1是不能放 if (x&s[k]) return false; else return true; } int count(int x) { int cnt=0; while (x) { cnt++; x&=(x-1);//去掉最后一个1 } return cnt; } void Dp() { memset(tmp,-1,sizeof(tmp)); //预处理第一行 for (int j=1; j<=tot; j++) if ( check2(state[j],1) ) { tmp[j][1]=num[j]; if (tmp[j][1]>ans) ans=tmp[j][1]; } for (int i=2; i<=m; i++) { for (int j=1; j<=tot; j++) //当前行 { if (!check2(state[j],i)) continue; for (int k=1; k<=tot; k++) //上一行 { if ( state[k]&state[j] ) continue; for (int t=1; t<=tot; t++) { if (state[t]&state[j]) continue; if (tmp[k][t]==-1) continue; f[j][k]=max(f[j][k],tmp[k][t]+num[j]); if (f[j][k]>ans) ans=f[j][k]; } } } memcpy(tmp,f,sizeof(f)); } } int main() { freopen("dp.in","r",stdin); freopen("dp.out","w",stdout); scanf("%d%d",&m,&n); char tmp; for (int i=1; i<=m; i++) { s[i]=0; scanf("%*c"); for (int j=1; j<=n; j++) { scanf("%c",&tmp); if (tmp=='H') s[i]+=1<<(n-j); } } //Search all states int limit=(1<<n)-1; for (int i=0; i<=limit; i++) if ( check(i) ) { state[++tot]=i; num[tot]=count(i); } Dp(); printf("%d\n",ans); fclose(stdin); fclose(stdout); return 0; }