zoukankan      html  css  js  c++  java
  • 状态压缩dp入门

    poj1321 http://poj.org/problem?id=1321

    我们可以把棋盘的每一行看做是一个状态,如果某一列放置了棋子,那么就标记为1,否则就标记为0.然后把它看成是一个二进制数,然后转为10进制数,就可以当做数组下标然后进行状态转移了

    设dp[i][s] 为处理到第i行时,状态为s的方法数

    那么我们枚举第i-1行的所有状态s

    dp[i][s] += dp[i-1][s]; //表示第i行不放置棋子的方法数

    dp[i][s|(1<<j)] += dp[i-1][s] //表示第i行第j列放置棋子的方法数   (前提是 chess[i][j]=='#' && 状态s第j列没有放过棋子)

     1 #include <stdio.h>
     2 #include <string.h>
     3 char chess[9][9];int dp[11][1<<8];
     4 int n,k;
     5 int main()
     6 {
     7     int i,j,s,ss;
     8     while(scanf("%d%d",&n,&k)!=EOF)
     9     {
    10         int ans = 0;
    11         if(n==-1) return 0;
    12         memset(dp,0,sizeof(dp));
    13         for(i=1; i<=n; ++i)
    14         {
    15             scanf("%s",chess[i]);
    16         }
    17         dp[0][0] = 1;
    18         for(i=1; i<=n; ++i)
    19             for(s=0; s<(1<<n); ++s)
    20             {
    21                 
    22                 for(j=0; j<n; ++j)
    23                     if(chess[i][j]=='#'&&((1<<j)&s)==0)
    24                         dp[i][s|1<<j] += dp[i-1][s];//表示第i行第j列放置棋子的方法数  
    25                 dp[i][s] += dp[i-1][s]; //表示第i行不放置棋子的方法数
    26             }
    27         ///for(j=1; j<=n; ++j)
    28         for(s=0; s<(1<<n); ++s)
    29         {
    30             i = s;
    31             int cnt = 0 ;
    32             for(;i;i-= i&-i)//统计状态s有多少个1,表示放了多少个棋子
    33                 cnt ++;
    34             if(cnt==k)
    35                 ans+= dp[n][s];
    36         }
    37         printf("%d
    ",ans);
    38     }
    39     return 0;
    40 }

     poj3254 http://poj.org/problem?id=3254

    本来这一题也想用上面那题的思路做,可是后来发现不行,因为上面那题一行只选择一个位置,而这题一行可以选择多个位置。

    那么我们这题可以枚举多个位置,如果和上一行的状态不冲突,那么上一行的状态就能转移到这一行

     1 #include <stdio.h>
     2 #include <string.h>
     3 
     4 int field[13];
     5 int situation[13];
     6 int dp[13][1<<13];
     7 const int MOD = 100000000;
     8 bool isOk(int s)
     9 {
    10     if(s&(s>>1)) return false;
    11     return  true;
    12 }
    13 
    14 int main()
    15 {
    16     int n,m,i,j,s,ss,cnt,ans;
    17     while(scanf("%d%d",&n,&m)!=EOF)
    18     {
    19         ans = cnt = 0;
    20         memset(dp,0,sizeof(dp));
    21         memset(field,0,sizeof(field));
    22         for(s=0; s<(1<<m); ++s) if(isOk(s)) situation[cnt++] = s;//记录所有可行的状态,即不能有相邻的1
    23         for(i=0; i<n; ++i)
    24             for(j=0; j<m; ++j)
    25             {
    26                 scanf("%d",&s);
    27                 field[i] |= (!s)<<j;//这里这样存的含义是,如果situation[] & field[] 不为0,说明状态situation不行
    28             }    
    29         
    30         //dp[i][j] 表示处理完前i行,第j个状态的方法数
    31         for(i=0; i<cnt; ++i)
    32             if( !(situation[i] & field[0]))
    33                 dp[0][i] = 1;
    34             
    35         for(i=1; i<n; ++i)
    36             for(s=0; s<cnt; ++s)
    37             {
    38                 if(situation[s]&field[i]) continue;//这一行的状态要合法
    39                 for(ss=0; ss<cnt; ++ss)
    40                 {
    41                     if(situation[ss] & situation[s]) continue;//这一行的状态和上一行的状态不合法
    42                     dp[i][s] += dp[i-1][ss];//只要上一行的状态ss和这一行的状态s不冲突,那么就能转化为状态s
    43                     dp[i][s] %= MOD;
    44                 }
    45             }
    46         for(i=0; i<cnt; ++i)
    47                 ans = (ans + dp[n-1][i]) % MOD;
    48         printf("%d
    ",ans);
    49 
    50     }
    51     return 0;
    52 }

    poj1185 http://poj.org/problem?id=1185

    和上面那题差不多,只是当前行的状态不止和上一行有关,还和上上一行有关,所以数组要多开一维

    奇怪的是为什么用scanf("%c");G++ac,C++wa    用scanf("%s")G++和C++都wa

     1 /*
     2 分析:一行的状态不能有 11(连续的两个1)   也能有101(隔一个空然后出现一个1)
     3 dp[i][j][k]  表示第i行状态为j第i-1行状态为k时,最多有多少个炮兵摆放
     4 dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][t]+num[j]);
     5 */
     6 
     7 #include <stdio.h>
     8 #include <string.h>
     9 int situation[11];
    10 char matrix[111][11];
    11 int map[111];
    12 int num[111];
    13 int dp[111][100][100];
    14 bool isOk(int s)
    15 {
    16     if(s&(s<<1)) return false;
    17     if(s&(s<<2)) return false;
    18     return true;
    19 }
    20 
    21 int count(int s)
    22 {
    23     int ret = 0;
    24     for(;s; s-= (s&-s))//统计状态s有多少个1
    25         ret++;
    26     return ret;
    27 }
    28 inline int max(const int &a, const int &b)
    29 {
    30     return a < b ? b : a;
    31 }
    32 int main()
    33 {
    34     int n,m,i,j,k,s,cnt,ans,t;
    35     char ch;
    36     while(scanf("%d%d",&n,&m)!=EOF)
    37     {
    38         cnt = ans = 0;
    39         memset(dp,-1,sizeof(dp));
    40         for(s=0; s<(1<<m); ++s) if(isOk(s)) 
    41         {
    42             num[cnt] = count(s);
    43             situation[cnt++] = s;
    44         }
    45         
    46         for(i=0; i<n; ++i)
    47         {
    48             
    49             getchar();
    50             map[i] = 0;
    51             for(j=0; j<m; ++j)
    52             {
    53                 scanf("%c",&ch);
    54                 if(ch=='H') map[i] |= 1<<j;
    55             }
    56         }
    57         for(i=0; i<cnt; ++i)
    58             if(!(situation[i] & map[0]))
    59                 dp[0][i][0] = num[i];
    60         for(i=1; i<n; ++i)
    61             for(j=0; j<cnt; ++j)
    62             {
    63                 if(situation[j] & map[i]) continue;
    64                 for(k=0; k<cnt; ++k)
    65                 {
    66                     if(situation[j] & situation[k]) continue;
    67                     for(t=0 ;t<cnt; ++t)
    68                     {
    69                         if(situation[j] & situation[t]) continue;
    70                         if(dp[i-1][k][t]==-1) continue;
    71                         dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][t] + num[j]);
    72                     }
    73                 }
    74             }
    75         for(i=0; i<n; ++i)
    76             for(j=0; j<cnt; ++j)
    77                 for(k=0; k<cnt; ++k)
    78                     ans = max(ans,dp[i][j][k]);
    79         printf("%d
    ",ans);
    80     }
    81     
    82     return 0;
    83 }

     hdu3001 http://acm.hdu.edu.cn/showproblem.php?pid=3001

    平常的状态压缩,都是某个位置放(1)或者不放(0),所以可以直接用二进制进行压缩
    但是这题,每个点可以走两次,那么就可以标记为0,1,2 所以要用三进制进行压缩。
    即用一个数组存储每个状态的三进制的每位

     1 #include <stdio.h>
     2 #include <string.h>
     3 const int INF = 1<<30;
     4 int three[11],situation[59049][11],edge[11][11],dp[59049][11];
     5 void init()
     6 {
     7     int i,t,cnt;
     8     three[0] = 1;
     9     for(i=1; i<=10; ++i)
    10         three[i] = three[i-1] * 3;
    11     for(i=0; i<59049; ++i)
    12     {
    13          t = i;
    14          cnt = 0;
    15          while(t)//存储状态的三进制的每一位
    16          {
    17              situation[i][cnt++] = t % 3;
    18              t /= 3;
    19          }
    20     }
    21 }
    22 inline int min(const int &a, const int &b)
    23 {
    24     return a < b ? a : b;
    25 }
    26 int main()
    27 {
    28     init();
    29     int n,m,i,j,a,b,c,ans,s;
    30     while(scanf("%d%d",&n,&m)!=EOF)
    31     {
    32         
    33         for(i=0; i<n; ++i)
    34             for(j=0; j<n; ++j)
    35                 edge[i][j] = INF;
    36         for(i=0; i<n; ++i)
    37             for(s=0; s<three[n]; ++s)
    38                 dp[s][i] = INF;
    39         ans = INF;
    40         for(i=0; i<m; ++i)
    41         {
    42             scanf("%d%d%d",&a,&b,&c);
    43             a--,b--;
    44             if(edge[a][b] > c)
    45                 edge[a][b] = edge[b][a] = c;
    46         }
    47         for(i=0; i<n; ++i) dp[three[i]][i] = 0;//处于源点的距离为0
    48         //因为走过某些城市可以是任意组合的,所以枚举这些状态
    49         for(s=0; s<three[n]; ++s)
    50         {
    51             bool flag = true;
    52             for(i=0; i<n; ++i)
    53             {
    54                 if(situation[s][i]==0) flag = false;//状态s的第i位为0,说明没有走过所有的城市
    55                 if(dp[s][i]==INF) continue;
    56                 for(j=0; j<n; ++j)
    57                 {
    58                     if(i==j) continue;
    59                     if(edge[i][j]==INF || situation[s][j]==2) continue;
    60                     int nextS = s + three[j];
    61                     dp[nextS][j] = min(dp[nextS][j],dp[s][i] + edge[i][j]);
    62                 }
    63             }        
    64             if(flag)//走过所有的状态
    65             {
    66                 for(i=0; i<n; ++i)
    67                     ans = min(ans,dp[s][i]);
    68             }
    69         }
    70         if(ans == INF)
    71             printf("-1
    ");
    72         else
    73         printf("%d
    ",ans);
    74     }
    75     return 0;
    76 }
  • 相关阅读:
    Day Five
    Day Four
    JS中attr和prop区别
    layui单选框radio使用form.render() 更新渲染失效的原因
    MySql的时区(serverTimezone)问题
    com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的区别 serverTimezone设定
    idea使用maven下载jar包,出现证书校验问题问题,unable to find valid certification path to requested target
    java实体类为什么要实现Serializeable 序列化呢?
    IntelliJ IDEA 2017 提示“Unmapped Spring configuration files found.Please configure Spring facet.”解决办法
    JS三个等号"==="是什么意思
  • 原文地址:https://www.cnblogs.com/justPassBy/p/4285851.html
Copyright © 2011-2022 走看看