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

     

  • 相关阅读:
    存储引擎的优缺点及增删改查基本操作
    安装Mariadb
    Mysql 入门概念
    Nginx语法着色
    find用法,文件压缩和lsof和cpio
    软件包管理
    Django 生成六位随机图片验证码
    Django自定义过滤器和自定义标签
    Django零碎知识点
    jQuery实现淡入淡出样式轮播
  • 原文地址:https://www.cnblogs.com/shuzy/p/3351700.html
Copyright © 2011-2022 走看看