zoukankan      html  css  js  c++  java
  • 状态压缩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

     

  • 相关阅读:
    UVA 11488 Hyper Prefix Sets (字典树)
    UVALive 3295 Counting Triangles
    POJ 2752 Seek the Name, Seek the Fame (KMP)
    UVA 11584 Partitioning by Palindromes (字符串区间dp)
    UVA 11100 The Trip, 2007 (贪心)
    JXNU暑期选拔赛
    计蒜客---N的-2进制表示
    计蒜客---线段的总长
    计蒜客---最大质因数
    JustOj 2009: P1016 (dp)
  • 原文地址:https://www.cnblogs.com/shuzy/p/3351700.html
Copyright © 2011-2022 走看看