状态压缩DP专题
hdu 1565 方格取数(1)
题目大意:取不能有临边的点,求最大和
分析:把每一列转化为2进制每一位,1表示这个格子取,0为不取,通过二进制得到的整数值表示一个状态。
这边判断没有临边的方法即x&y==0同时x与y的二进制也没有相邻的1。即x&(x<<1)==0
用dp[i][j]表示第i行状态为j的最大和,那么它是由前面的i-1行的状态过来的。
方程为:dp[i][j]=max{dp[i][j],dp[i-1][k]+sum[j]}其中sum[j]表示在i行的状态为j取到数的和
最后结果为max{dp[n][i]} i为状态
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define maxlen 18000 5 using namespace std; 6 int dp[21][maxlen]; 7 int status[maxlen]; 8 int maps[21][21]; 9 int main () 10 { 11 int n; 12 while(scanf("%d",&n)!=EOF) 13 { 14 int cnt=0; 15 for(int i=0;i<(1<<n);++i) 16 { 17 if((i&(i<<1))==0) 18 status[cnt++]=i; 19 } 20 for(int i=1;i<=n;++i) 21 { 22 for(int j=1;j<=n;++j) 23 { 24 scanf("%d",&maps[i][j]); 25 } 26 } 27 memset(dp,0,sizeof(dp)); 28 for(int i=1;i<=n;++i) 29 { 30 for(int j=0;j<cnt;++j) 31 { 32 int ans=0; 33 for(int k=0;k<n;++k) 34 { 35 if((status[j]&(1<<k))!=0) 36 { 37 ans += maps[i][k+1]; 38 } 39 } 40 dp[i][j]=ans; 41 for(int k=0;k<cnt;++k) 42 { 43 if((status[j]&status[k])==0) 44 dp[i][j]=max(dp[i][j],dp[i-1][k]+ans); 45 } 46 } 47 } 48 int ans=-1; 49 for(int i=0;i<cnt;++i) 50 { 51 ans=max(ans,dp[n][i]); 52 } 53 printf("%d\n",ans); 54 } 55 }
poj 1185 炮兵阵地
题目大意:炮兵的攻击范围如下图,要求不能有炮兵在其他炮兵的攻击范围内,部署的最大炮兵数。
![](http://poj.org/images/1185_1.jpg)
分析:给的地图大小最大为100*10所以可以跟上面的一样将每一列变为二进制每一位表示状态
那么主要就是判断状态的转移了。首先当前的状态只与前两行与有关,因为炮兵的攻击范围所限。
dp[i][j][k]表示第i行状态为k,上一行状态为j部署的最大值。如何转移?
dp[i][j][k]=max{dp[i][j][k],dp[i-1][l][j]+num[k]}其中l为i-2行的状态,num[k]为第i行状态为
k的时候的炮兵数,即k的二进制中1的个数。
枚举三重的状态,接下来的问题就是状态之间的判断是否合法了。这个具体看代码吧。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 int status [1<<11],num[1<<11]; 6 int cur[110]; 7 int dp[110][110][110]; 8 char maps[110][11]; 9 int n,m,cnt; 10 int Count(int x) 11 { 12 int ans=0; 13 while(x) 14 { 15 ans++; 16 x&=(x-1); 17 } 18 return ans; 19 } 20 void init() 21 { 22 for(int i=0; i<(1<<m); ++i) 23 { 24 if((i&(i<<1))||(i&(i<<2))) 25 continue; 26 status[cnt]=i; 27 num[cnt]=Count(i); 28 cnt++; 29 } 30 } 31 int judge(int x,int y) 32 { 33 return ((x&y)==0); 34 } 35 void work() 36 { 37 for(int i=0; i<cnt; ++i) 38 { 39 if(!judge(status[i],cur[1]))//第一行 40 continue; 41 dp[1][0][i]=num[i]; 42 } 43 for(int i=2; i<=n; ++i) 44 { 45 for(int j=0; j<cnt; ++j) //枚举上一行的状态 46 { 47 for(int k=0; k<cnt; ++k) //枚举当前行的状态 48 { 49 if(!judge(status[k],cur[i])||!judge(status[j],cur[i-1])||!judge(status[k],status[j])) 50 continue; 51 for(int l=0; l<cnt; ++l)//枚举i-2行的状态 52 { 53 if(!judge(status[l],cur[i-2])||!judge(status[k],status[l])||!judge(status[j],status[l])) 54 continue; 55 dp[i][j][k]=max(dp[i][j][k],dp[i-1][l][j]+num[k]); 56 } 57 } 58 } 59 } 60 int ans=0; 61 for(int j=0; j<cnt; ++j) 62 { 63 for(int k=0; k<cnt; ++k) 64 { 65 ans=max(ans,dp[n][j][k]); 66 } 67 } 68 printf("%d\n",ans); 69 } 70 int main () 71 { 72 while(scanf("%d%d",&n,&m)!=EOF) 73 { 74 75 for(int i=1; i<=n; ++i) 76 { 77 getchar(); 78 for(int j=1; j<=m; ++j) 79 { 80 scanf("%c",&maps[i][j]); 81 if(maps[i][j]=='H') 82 cur[i]+=1<<(m-j); 83 } 84 } 85 init(); 86 work(); 87 } 88 }
hdu 4539 郑厂长系列故事——排兵布阵
题目大意:跟上面类似,就是攻击的范围变为了距离其汉密顿距离为2,注意是刚好是2不是小于等于2
分析:跟上面的一样的方法,就是状态的判合法不一样。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 int status [210],num[210]; 6 int cur[110]; 7 int dp[110][210][210]; 8 int maps[110][11]; 9 int n,m,cnt; 10 int Count(int x) 11 { 12 int ans=0; 13 while(x) 14 { 15 ans++; 16 x&=(x-1); 17 } 18 return ans; 19 } 20 void init() 21 { 22 memset(status,0,sizeof(status)); 23 memset(num,0,sizeof(num)); 24 cnt=0; 25 for(int i=0; i<(1<<m); ++i) 26 { 27 if((i&(i<<2))||(i&(i>>2))) 28 continue; 29 status[cnt]=i; 30 num[cnt]=Count(i); 31 cnt++; 32 } 33 } 34 int judge(int x,int y) 35 { 36 return ((x&y)==0); 37 } 38 void work() 39 { 40 memset(dp,-1,sizeof(dp)); 41 for(int i=0; i<cnt; ++i) 42 { 43 if(judge(status[i],cur[0])) 44 dp[0][i][0]=num[i]; 45 } 46 for( int i=1; i<n; i++ ) 47 { 48 for( int j=0; j<cnt; j++ ) 49 { 50 if( (status[j]&cur[i])==0 ) 51 { 52 for( int k=0; k<cnt; k++ ) 53 { 54 if( (status[j]&(status[k]<<1))==0&&(status[j]&(status[k]>>1))==0 ) 55 { 56 for( int l=0; l<cnt; l++ ) 57 { 58 if( dp[i-1][k][l]==-1 ) continue; 59 if( (status[j]&status[l])==0&&(status[k]&(status[l]>>1))==0&&(status[k]&(status[l]<<1))==0 ) 60 dp[i][j][k] = max( dp[i][j][k],dp[i-1][k][l]+num[j] ); 61 } 62 } 63 } 64 } 65 } 66 } 67 int ans=0; 68 for(int i=0; i<cnt; ++i) 69 { 70 for(int j=0; j<cnt; ++j) 71 { 72 ans=max(ans,dp[n-1][i][j]); 73 } 74 } 75 printf("%d\n",ans); 76 } 77 int main () 78 { 79 while(scanf("%d%d",&n,&m)!=EOF) 80 { 81 memset(cur,0,sizeof(cur)); 82 for(int i=0; i<n; ++i) 83 { 84 for(int j=0; j<m; ++j) 85 { 86 scanf("%d",&maps[i][j]); 87 if(maps[i][j]==0) 88 cur[i]|=(1<<j); 89 } 90 } 91 init(); 92 work(); 93 } 94 }
hdu 4739 Zhuge Liang's Mines
题目大意:给你n个点的坐标,问最多可以选择多少个点可以组成正方形,不能重复选择。
分析:
状态压缩,其实也是十分简单,一共就20个点,转化为二进制后即可以用一个整数表示整个状态。
dp[s]表示s这个状态下最多的正方形数。状态的转移也很简单。
dp[s]=max(dp[s],dp[s']+1)
如果可以组成正方形即可转到另一个状态。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define maxlen 1<<21 6 using namespace std; 7 int n; 8 int dp[maxlen]; 9 int used[30]; 10 struct node 11 { 12 int x,y; 13 }p[30]; 14 bool cmp(node a,node b) 15 { 16 return a.x==b.x?a.y<b.y:a.x<b.x; 17 } 18 bool judge(node a,node b,node c,node d) 19 { 20 return (a.x==b.x&&a.y==c.y&&c.x==d.x&&b.y==d.y&&b.y-a.y==c.x-a.x); 21 } 22 int dfs(int s) 23 { 24 if(s<0) 25 return 0; 26 if(dp[s]) 27 return dp[s]; 28 for(int i=0;i<n;++i) 29 { 30 for(int j=i+1;j<n;++j) 31 { 32 for(int k=j+1;k<n;++k) 33 { 34 for(int l=k+1;l<n;++l) 35 { 36 if(judge(p[i],p[j],p[k],p[l])&&!used[i]&&!used[j]&&!used[k]&&!used[l]) 37 { 38 used[i]=used[j]=used[k]=used[l]=true; 39 int x = s-(1<<i)-(i<<j)-(1<<k)-(1<<l); 40 dp[s] = max(dp[s],1+dfs(x)); 41 used[i]=used[j]=used[k]=used[l]=false; 42 } 43 } 44 } 45 } 46 } 47 return dp[s]; 48 } 49 int main () 50 { 51 while(scanf("%d",&n)!=EOF) 52 { 53 if(n==-1) 54 break; 55 for(int i=0;i<n;++i) 56 scanf("%d%d",&p[i].x,&p[i].y); 57 sort(p,p+n,cmp); 58 memset(dp,0,sizeof(dp)); 59 memset(used,0,sizeof(used)); 60 int ans = dfs((1<<n)-1); 61 printf("%d\n",4*ans); 62 } 63 }