因为不知道不同的博客怎么转,就把别人的复制过来了,这个题解写的非常好,原地址为:

http://hi.baidu.com/wangxustf/item/9138f80ce2292b8903ce1bc7

分类:DP

题目分析与算法模型

因为行N <= 100 列M <= 10
我们就在10上面做文章。

在任意一行上,最多10列,假设这10个位置都是平原,那么在这10个位置上放置炮兵并互不攻击,一共有60种方法(可以算一下)。具体是哪60种方法,这个需要枚举一下。我们用num[i]表示第i种方案放置的炮兵数量。
比如:
PPPPHHPPPP
我们可以在第1个位置和第7个位置各放一个炮兵,这是一种可行的方案。此时num[i] = 2

假设一共有N行,
对于第s-2行,它有60种方案
对于第s-1行,它也有60种方案
对于第s行,它也有60种方案
对于第s+1行,它也有60种方案
.......

我们用dp[s][i][j]表示第s行使用第i种方案,第s-1行使用第j种方案时部队可以部署的最大值.那么max{dp[N][i][j], 其中i,j=1...60}的就是最后的答案

这里需要检验状态i和j是否互相兼容,这个可以通过连个状态的十进制表示的两个数间的&运算来判断(如果兼容,那么运算后的值必为0)
如果兼容,可以推出:
dp[s][i][j] = num[i] + max{dp[s-1][j][k], 其中k=1...60,并且i和k状态也时兼容的}
这表示:第s行使用第i种方案,第s-1行使用第j种方案时,我们枚举第s-2行的方案k。如果i,j,k这三种方案之间是相容的,那么dp[s][i][j] = num[i] + dp[s-1][j][k]

代码:

#include<stdio.h>
#include<string.h>
int cnt,m,n;
int dp[100][64][64],num[64],state[64],bitmap[100];
void init()
{
    int tmp;
    cnt=0;
    for(int i=0;i<(1<<m);i++)  //枚举每行的状态,从0到2^m - 1,判断其是否合法
    {
        tmp=i;
        if( ((tmp<<1)&i) | ((tmp<<2)&i) ) continue;//判断该行在这个状态时是否合法(任意炮兵都不在其他炮兵的攻击范围之内)
        state[cnt]=i;   //通过数组state[]记录合法的状态(十进制表示)
        num[cnt]=tmp&1;  //num[]数组记录这个合法状态下‘1’的个数(也就时炮兵的个数)
        while( tmp = (tmp>>1) )
            num[cnt]+=tmp&1;
        cnt++;             //此函数的统计是假设当该行都为平地时,即共有cnt+1个合法状态
    }    
}
void solve()
{
    int ans,i,j,k,p;
    memset(dp,0,sizeof(dp));
    for(i=0;i<n;i++)                         //枚举每一行
        for(j=0;j<cnt;j++)                  //先枚举第i行的可能状态
        {
            if(bitmap[i]&state[j]) continue;  //地图中标记为“山地”的点不能布兵
            if(i==0)  dp[i][j][0]=num[j];  
            else if(i==1)
            {
                for(k=0;k<cnt;k++)                  //枚举第i-1行的可能状态
                {
                    if(bitmap[i-1]&state[k]) continue;  //判断第i-1行的k状态是否和山地冲突,冲突就跳到下一个状态k+1
                    if(state[j]&state[k]) continue;    //判断上下两行(i-1和i)的合法状态是否兼容彼此
                    if(dp[i][j][k]<dp[i-1][k][0]+num[j])
                        dp[i][j][k]=dp[i-1][k][0]+num[j];
                }
            }
            else
            {
                for(k=0;k<cnt;k++)                 //枚举第i-1行的可能状态
                {                              
                    if(bitmap[i-1]&state[k]) continue;   //判断第i-1行的k状态是否和山地冲突,冲突就跳到下一个状态k+1
                    if(state[j]&state[k]) continue;        //判断上下两行(i-1和i)的合法状态是否兼容彼此
                    for(p=0;p<cnt;p++)                 //枚举第i-2行的可能状态
                    {
                        if(bitmap[i-2]&state[p]) continue; //判断第i-2行的p状态是否和山地冲突,冲突就跳到下一个状态p+1
                        if(state[k]&state[p] || state[j]&state[p]) continue;//判断上下两行(i-2和i-1,i-2和i)的合法状态是否兼容彼此
                        if(dp[i][j][k]<dp[i-1][k][p]+num[j])
                            dp[i][j][k]=dp[i-1][k][p]+num[j];
                    }
                }
            }
        }
    
  ans=0;
    //for(i=0;i<n;i++)
        for(j=0;j<cnt;j++)
            for(k=0;k<cnt;k++)
                if(dp[n-1][j][k]>ans)
                    ans=dp[n-1][j][k];
    printf("%d\n",ans);
}
int main()
{
    char s[12];
 int i,j;
    while(scanf("%d%d",&n,&m)!=EOF)
 {
     memset(bitmap,0,sizeof(bitmap));
  for(i=0;i<n;i++)
  {
   scanf("%s",s);
            for(j=0;j<m;j++)if(s[j]=='H')bitmap[i]+=(1<<(m-1-j));
    //if(s[j]=='H')bitmap[i]|=(1<<j);
  }
        init();
        solve();
 }
 return 0;
    
}

 

/*算法回顾:
       压缩状态的动态规划
       1.利用二进制表示某个位置有无炮兵状态
       2.把任意一行看成一个二进制串,并转化为相应的十进制数,即该行的压缩状态
       3.枚举该行(i)所有二进制串,找出单独一行的所有可能的状态,即合法的压缩状态(s[i])
       4.利用滚动数组降低内存消耗
 技巧:     二进制的位运算,滚动数组
 动规方程:   f[i][j][k] = max{f[i-1][k][l]+c[j]}, 
    f[i][j][k]表示第i行状态为s[j],第i-1行状态为s[k]的最大炮兵数
             枚举l的每种状态,且s[j],s[k],s[l],地形互不冲突
*/

POJ1185 炮兵阵地 状态压缩的更多相关文章

  1. POJ1185 - 炮兵阵地(状态压缩DP)

    题目大意 中文的..直接搬过来... 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平 ...

  2. POJ1185炮兵阵地(状态压缩 + dp)

    题目链接 题意:给出一张n * m的地图,其中 有的地方能放大炮,有的地方不能,大炮与上下左右两个单位范围内会相互攻击,问最多能放几个大炮 能放大炮为1不能放大炮为0,把每一行看做一个状态,要除去同一 ...

  3. POJ - 1185 炮兵阵地 (状态压缩)

    题目大意:中文题目就不多说大意了 解题思路: 1.每行最多仅仅有十个位置,且不是山地就是平原,那么就能够用1表示山地,0表示平原,将每一行的状态进行压缩了 2.接着找出每行能放炮兵的状态.先不考虑其它 ...

  4. [P2704][NOI2001]炮兵阵地 (状态压缩)

    最近抄状压的代码…… 然后盯上了这个题目 调试了一个晚上,终于A了 但是是对着宝典打的,我依然不懂状态压缩 那么下一步先把装压放一放,学一下树形DP吧 #include<cstdio> # ...

  5. luogu2704 炮兵阵地 状态压缩DP

    题目大意:一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),在每一格平原地形上最多可以布置一支炮兵部队,能攻击到的区域:沿横向左右各两格,沿纵向上 ...

  6. poj 1185 炮兵阵地 状态压缩dp

    思路:定义一个三维数组dp[x][i][j]其中x为now和pre两种状态,now表示当前两行最优解,pre表示出了本行外,前两行的最优解.那么状态转移方程为 dp[now][j][k]=max(dp ...

  7. POJ 3254 炮兵阵地(状态压缩DP)

    题意:由方格组成的矩阵,每个方格可以放大炮用P表示,不可以放大炮用H表示,求放最多的大炮,大炮与大炮间不会互相攻击.大炮的攻击范围为两个方格. 分析:这次当前行的状态不仅和上一行有关,还和上上行有关, ...

  8. POJ1185炮兵阵地(状态压缩DP)

    POJ飞翔.数据弱 ZQOJ飞翔 数据强 Description 司令部的将军们打算在N×M的网格地图上部署他们的炮兵部队.一个N×M的地图由N行M列组成,地图的每一格可能是山地(用"H&q ...

  9. [poj1185]炮兵阵地_状压dp

    炮兵阵地 poj-1185 题目大意:给出n列m行,在其中添加炮兵,问最多能加的炮兵数. 注释:n<=100,m<=10.然后只能在平原的地方建立炮兵. 想法:第2到状压dp,++.这题显 ...

随机推荐

  1. js常用DOM操作

    在博客园看到了苏夏写的常用DOM整理文章,地址:http://www.cnblogs.com/cabbagen/p/4579412.html,然后抽时间都试了一下这些常用的DOM操作.在这里记录一下. ...

  2. MySQL 有输入输出参数的存储过程实例

    1.MySQL 有输入输出参数的存储过程实例 DELIMITER // DROP PROCEDURE IF EXISTS `test`.`p_getvalue` // CREATE PROCEDURE ...

  3. 四轴飞行器1.2.1 RT-Thread 环境搭建

    买的飞控板到了,开始写下位机的程序了,本来打算用UCOS的,因为以前用过,比较熟悉,可以很快上手,不过板子的卖家推荐了RT-Thread,以前虽然有接触过,但是没用过,于是去官网看了下,感觉还不错.其 ...

  4. linux下实现两人、三人无序对话功能

    序:引子 对话功能实际上就是利用管道见得通信.最原始的是一方发另一方收,不能进项交互,发送方的代码如下: /*============================================ ...

  5. 并查集及其简单应用:优化kruskal算法

    并查集是一种可以在较短的时间内进行集合的查找与合并的树形数据结构 每次合并只需将两棵树的根合并即可 通过路径压缩减小每颗树的深度可以使查找祖先的速度加快不少 代码如下: int getfather(i ...

  6. 在自定义的js验证规则中调用magento的VarienForm方法验证表单

    js部分<script type="text/javascript"> //<![CDATA[ var loginForm = new VarienForm('l ...

  7. UNIX网络编程---传输层:TCP、UDP、SCTP(二)

    UNIX网络编程----传输层:TCP.UDP.SCTP 一.概述 本章的焦点是传输层:包括TCP.UDP.和SCTP(流控制传输协议).SCTP是一个较新的协议,最初设计用于跨因特网传输电话信令. ...

  8. jQuery+PHP实现的砸金蛋中奖程序

    准备 我们需要准备道具(素材),即相关图片,包括金蛋图片.蛋砸碎后的图片.砸碎后的碎花图片.以及锤子图片. HTML 我们页面上要展现的是一个砸金蛋的台子,台上放了编号为1,2,3的三个金蛋,以及一把 ...

  9. Java基础学习笔记2

    运算符: 重点:++和--运算符; a++ (a--):表示先将a的原值带入计算,计算完毕后,再将a的值进行+1(-1); ++a (--a):先将a的值进行+1(-1)运算,然后将+1(-1)以后的 ...

  10. C#核编之System.Environment类

    在前面的例子中用来了Environment.GetCommandLineArgs()这个方法,这个方法就是获取用户的命令行输入,是Environment类的方法之一,该方法的返回值是string[]  ...