zoukankan      html  css  js  c++  java
  • POJ 1185 状态压缩DP(转)

    1. 为何状态压缩:

        棋盘规模为n*m,且m≤10,如果用一个int表示一行上棋子的状态,足以表示m≤10所要求的范围。故想到用int s[num]。至于开多大的数组,可以自己用DFS搜索试试看;也可以遍历0~2^m-1,对每个数值的二进制表示进行检查;也可以用数学方法(?)

     

    2. 如何构造状态:

        当然,在此之前首先要想到用DP(?)。之后,才考虑去构造状态函数f(...)。

        这里有一个链式的限制 :某行上的某个棋子的攻击范围是2。即,第r行的状态s[i],决定第r-1行只能取部分状态s[p];同时,第r行的状态s[i],第r-1行状态s[p],共同决定第r-2行只能取更少的状态s[q]。当然,最后对上面得到的候选s[i], s[p], s[q],还要用地形的限制去筛选一下即可。

        简言之,第r行的威震第r-2行,因此在递推公式(左边=右边)中,必然同时出现r,和r-2两个行标;由于递推公式中行标是连续出现的,故在递推公式中必然同时出现r, r-1和r-2三个行标。由于在递推公式中左边包含一个f(...),右边包含另一个f(...),根据抽屉原理,r, r-1, r-2中至少有两个在同一个f(...)中,因此状态函数中必然至少包括相邻两行的行号作为两个维度。这就是为什么状态函数要涉及到两(相邻的)行,而不是一行。能想到的最简单形式如下:

        dp[r][i][p]:第r行状态为s[i],第r-1行状态为s[p],此时从第0行~第r行棋子的最大数目为dp[r][i][p]

     

        递推公式:

     

                                                    s[p]影响到s[q]的选取

                                                              ----

                                                             |    |

                      dp[r][i][p]=max{dp[r-1][p][q]}+sum[j], 其中sum[j]是状态s[j]中1的个数

                               |   |                             |

                               ----                             |

                   s[i]影响到s[p]的选取                 |

                               |                                 |

                               ----------------------------

     


    代码如下:

      1 #include <stdio.h> 
      2 #include <string.h> 
      3 #include <iostream> 
      4 #define MAX(a,b) (a)>(b)?(a):(b) 
      5 using namespace std; 
      6 int dp[105][65][65];    //d[i][j][k]: “第i行状态是s[j],第i-1行状态是s[k]”的
      7 int s[105];    //一行的状态选择s[0], s[1], ... , s[k-1]
      8 int n,m;    //n行×m列
      9 int k;    //一行的所有状态数
     10 int map[105];    //'H''P'地图map[0]~map[n-1],地图每一行map[line]: 1001 表示HPPH
     11 int sum[105]; 
     12 
     13 /*
     14     很久就看推荐题目有这个了,一直没做,因为看了好几次没看懂,都说dp,这几天看了状态压缩后明白了,其实就是用
     15     二进制来表示各个位置的状态然后进行枚举,把状态放进数组里就行,在这里用dp[i][j][k]表示第i行,当前j状态,
     16     i-1行是k状态时候的最大炮数    dp[i][j][k]=MAX(dp[i][j][k],dp[i-1][k][p]+sum[j])
     17 
     18     CAUTION:
     19     1. 所有下标均从0开始
     20     2. m<=10保证了可以用一个int存储一行的状态
     21 */
     22 
     23 //状态s[x]是否造成行冲突
     24 bool ok(int x) 
     25 { 
     26     if(x&(x<<1))return false;
     27     if(x&(x<<2))return false; 
     28     return true;
     29 } 
     30 
     31 //状态s[x]下有多少个1 
     32 int getsum(int x)
     33 { 
     34     int num=0; 
     35     while(x>0) 
     36     { 
     37         if(x&1)num++; 
     38         x>>=1; 
     39     } 
     40     return num; 
     41 } 
     42 
     43 void find() 
     44 { 
     45     memset(s,0,sizeof(s)); 
     46     for(int i=0;i<(1<<m);i++) //i枚举所有m位的二进制数
     47     { 
     48         if(ok(i)) 
     49         { 
     50             s[k]=i; 
     51             sum[k++]=getsum(i); 
     52         } 
     53   
     54     } 
     55 } 
     56   
     57 int main() 
     58 { 
     59     while(scanf("%d%d",&n,&m)!=EOF){ 
     60         memset(dp,-1,sizeof(dp)); 
     61 
     62         int i;
     63         for(i=0;i<n;i++){ 
     64              for(int j=0;j<m;j++){ 
     65                 char tmp; 
     66                 cin>>tmp; 
     67                 if(tmp=='H')map[i]=map[i]|(1<<j);//把第i行原始状态取反后放入map[i] 
     68             } 
     69         } 
     70 
     71         k=0; 
     72         find(); 
     73 
     74         //1. 初始化第0行状态(只考虑有效状态,无效状态为-1)
     75         for(i=0;i<k;i++)
     76             if(!(s[i]&map[0])) //s[i]为1的位如果对应平原(0),则&运算后为0
     77                 dp[0][i][0]=sum[i]; 
     78 
     79         //2. 计算第1~n-1行状态(碰到无效状态,continue)
     80         for(int r=1;r<n;r++) 
     81         { 
     82             for(int i=0;i<k;i++)//枚举第r行的状态 s[i]
     83             { 
     84                 if(map[r]&s[i])    continue;    //通过地形排除部分第r行的状态
     85 
     86                 for(int p=0;p<k;p++)    //枚举第r-1行状态 s[p]
     87                 { 
     88                     if(s[i] & s[p])    continue;    //r与r-1没有想接触的 
     89 
     90                     for(int q=0;q<k;q++)    //枚举第r-2行状态s[q]
     91                     { 
     92                          if(s[p] & s[q])    continue;   //Sam:这行是我加的
     93                          if(s[i] & s[q])    continue;    //r与r-2行没有接触的 
     94 
     95                          if(dp[r-1][p][q]==-1)    continue;    //所有不可能的情形dp[i][j][k]都为-1(初始化的值)
     96                          dp[r][i][p]=MAX(dp[r][i][p],dp[r-1][p][q]+sum[i]); 
     97                     }   
     98                 }    
     99             } 
    100         }
    101 
    102         int ans=0; 
    103         for(i=0;i<k;i++) 
    104             for(int j=0;j<k;j++) 
    105                 ans=MAX(ans,dp[n-1][i][j]); 
    106         printf("%d
    ",ans); 
    107     } 
    108 
    109     system("pause");
    110     return 0;
    111 }
  • 相关阅读:
    MybatisPlus自动填充公共字段的策略
    docker内的应用访问宿主机上的mysql和Redis
    Spingboot整合Redis,用注解(@Cacheable、@CacheEvict、@CachePut、@Caching)管理缓存
    集群中的session共享问题解决方案
    Java并发之原子性,有序性,可见性,以及Happen-Before原则
    Java NIO技术概述
    Java反射机制总结
    java线程以及定时任务
    java流概述以及文件读写示例
    CSS常用内容总结(二)
  • 原文地址:https://www.cnblogs.com/acm-bingzi/p/3278901.html
Copyright © 2011-2022 走看看