状态压缩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为状态

 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
",ans);
54     }
55 }
View Code

poj 1185 炮兵阵地

题目大意:炮兵的攻击范围如下图,要求不能有炮兵在其他炮兵的攻击范围内,部署的最大炮兵数。

 分析:给的地图大小最大为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的个数。

枚举三重的状态,接下来的问题就是状态之间的判断是否合法了。这个具体看代码吧。

 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
",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 }
View Code

hdu 4539 郑厂长系列故事——排兵布阵

题目大意:跟上面类似,就是攻击的范围变为了距离其汉密顿距离为2,注意是刚好是2不是小于等于2

分析:跟上面的一样的方法,就是状态的判合法不一样。

 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
",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 }
View Code

 hdu 4739 Zhuge Liang's Mines

题目大意:给你n个点的坐标,问最多可以选择多少个点可以组成正方形,不能重复选择。

分析:

状态压缩,其实也是十分简单,一共就20个点,转化为二进制后即可以用一个整数表示整个状态。

dp[s]表示s这个状态下最多的正方形数。状态的转移也很简单。

dp[s]=max(dp[s],dp[s']+1)

如果可以组成正方形即可转到另一个状态。

 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
",4*ans);
62     }
63 }
View Code

 

原文地址:https://www.cnblogs.com/shuzy/p/3351700.html