pku 1185 炮兵阵地 压缩dp 解题报告

本文详细介绍了如何使用压缩DP解决PKU1185炮兵阵地问题,通过二进制存储炮兵位置并利用每三行进行一次状态转移的方法来求解最大炮兵数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

pku 1185 炮兵阵地 解题报告

思路:用二进制保存这一行中炮兵,例如1 0 0 0, 1表示炮兵.那么很明显用到了压缩dp。认真发现炮兵摆放的规律可知,3行进行一个判断就可以了.也就是说,我们可以每3行都进行dp的转移,当然12行有特殊的处理.比如第i行为:1 0 0 0 0 i+1行为0 0 0 0 1,那么怎么知道i+2的状态呢?其实我们可以先将合法的状态枚举出来,这样问题就变得简单,我们枚举i+2d状态时就与i行、i+1行的状态进行比较.看看这个是否合法,当然i行与i+1行合法,但不一定与i+2合法,这时我们就必须用dp[i][j][k]来进行每3行的dp了。假设当前行i,i-1行的状态为j,i+2行的状态为k.

算法:

1、  map[i]保存i行存在山地的二进制状态;

2、  枚举一行(假设没有山地,也就是i行的枚举状态)的合法状态;

3、  特殊处理12行的情况;

4、  枚举判断当前行i的合法状态与map[i]的状态是否合法;再枚举i-1,i-2行这3行之间的合法状态.

5、  合法的话,进行dp转移:

dp[i][j][k] = max(dp[i][j][k],dp[i - 1][k][0] + num[j]);

AC代码:

#include <stdio.h>

#include <string.h>

#define N 101

#define M 11

#define max(a, b) (a > b ? a : b)

 

//dp[i][j][k]表示在第i行的合法状态为j,i-1行的合法状态为k,炮兵的数量

int dp[N][61][61];

int num[N], map[N], state[N], n, m, count;

 

void init()

{

    int i, temp;

 

    count = 0; 

       //枚举一行中都可能出现的状态,02^m - 1,判断其是否合法 

       //此函数的统计是假设当该行都为平地时,即共有cnt+1个合法状态

    for (i = 0; i < (1 << m); i++)      

    {

        temp = i;

        if (((temp << 1) & i) | ((temp << 2) & i))

              {

                     continue;                      //判断该行在这个状态时是否合法(任意炮兵都不在其他炮兵的攻击范围之内)

        }

              state[count] = i;                  //通过数组state[]记录合法的状态(十进制表示)

        num[count] = temp & 1;             //num[]数组记录这个合法状态下'1'的个数(也就是炮兵的个数)

        while (temp = (temp >> 1))

              {

                     num[count] += temp & 1;

              }

        count++;                         

    }   

}

 

void solve()

{

       int i, j, k, p;

 

       for (i = 0; i < n; i++)

       {

              for (j = 0; j < count; j++)

              {

                     //由于state[]记录的是一行的合法状态,那么用map[]&state[],则可以判断map[i]state[i]是合法的

                     if (map[i] & state[j])

                     {

                            continue;

                     }

                     //i=0i=1为特殊处理

                     if (i == 0)

                     {

                            dp[i][j][0] = num[j];

                     }

                     else if (i == 1)

                     { 

                            for (k = 0; k < count; k++)                  //枚举第0行的可能状态

                {

                                   if (state[j] & state[k])

                                   {

                                          continue;                            //判断上下两行(01)的合法状态是否兼容彼此

                    }

                                   dp[i][j][k] = max(dp[i][j][k], dp[i-1][k][0]+num[j]);

                }

                     }

                     else

                     {

                            //枚举第i行的所有可能状态

                            for (k = 0; k < count; k++)

                            {

                                   //判断上下两行(ii-1)的合法状态是否兼容彼此

                                   if (state[j] & state[k])

                                   {

                                          continue;

                                   }

                                   for (p = 0; p < count; p++)

                                   {

                                          //判断分别i行与i-2行的合法状态、i-1行与i-2行都是否彼此兼容

                                          if (state[k] & state[p] || state[j] & state[p])

                                          {

                                                 continue;

                                          }

                                          dp[i][j][k] = max(dp[i][j][k], dp[i-1][k][p]+num[j]);

                                   }

                            }

                     }

              }

       }

       int ans = 0;

       for (j = 0; j < count; j++)

       {

              for (k = 0; k < count; k++)

              {

                     if (dp[n - 1][j][k] > ans)

                     {

                            ans = dp[n - 1][j][k];

                     }

              }

       }

       printf("%d/n", ans);      

}

 

int main()

{

       freopen("1.txt", "r", stdin);

       int i, j;

       char s[11];

 

       scanf("%d%d", &n, &m);

       for (i = 0; i < n; i++)

       {

              scanf("%s", s);

              //将每一行出现的山地存储在二进制中

              for (j = 0; j < m; j++)

              {

                     if (s[j] == 'H')

                     {

                            map[i] += (1 << (m - 1 - j));

                     }

              }

       }

       init();

       solve();

 

       return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值