poj1185 炮兵阵地(状态压缩dp)

这题。。。换成3维后,果断更不好做了啊


首先说说为什么换成三维,dp数组有个特点,由前一个状态才能得到下一个状态,这样做的前提就是必须要保存前面状态的下标,而这个要兼顾前两行状态,所以由二维变为三维。变为三维后,就有个建模的过程。三维建模有两种,假设高为h,一种是直接立方体,另一种是h个矩形,这个题还是用立方体想比较好。


然后是数组的大小,这里有人说top为60,这个60怎么来的呢?二进制数一共10位,总共2^10 - 1,但其实一行最多放4个炮,所以就开始分类:

0个炮:1种;

1个炮:10种;

2个炮:?

3个炮:?

4个炮:1种。

重点是2和3个炮,(例如2个)这相当于先求当4对炮相邻有多少种方法,然后在乘C(2上, 5下)(不会插数学符号T T。。。)。3个炮也是类似,然而我现在不会求了,数学渣T T,所以只能留白了,以后复习了概率论再算吧。。。


再次是求动态方程。上一题玉米是求方案数,这一题是求最多放炮数,所以最后不需要累加,只是求最值就好(其实最大值也往往在数组下面)。

动态方程:dp[i][k][t] = max(dp[i][k][t], dp[i - 1][j][k] + num[t])。

注意t代表当前行,k代表上一行,j代表上上行,这样就好理解了。每次判断前的剪枝不要忘掉。


对了还有个位运算x &= (x - 1);统计1的个数,也比较经典,记住就好了。


说白了,这两道状压要在理解加记忆的基础上才能A掉,特别是这种长代码,哎,只能多做题加深印象了,总不能死记吧。。。

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>

using namespace std;

const int N = 60;
const int INF = 1e8;
const int mod = 100000000;

int n, m, top, total;
int state[N], cur[105], dp[105][N][N], num[N];

bool ok(int x)
{
    if(x & (x << 1)) return false;
    if(x & (x << 2)) return false;
    else return true;
}

void init()
{
    top = 0;
    total = 1 << m;
    for(int i = 0; i < total; i ++)
    {
        if(ok(i)) state[++ top] = i;
    }
}

bool fit(int x, int y)
{
    if(x & y) return false;
    else return true;
}

int jcount(int x)
{
    int cnt = 0;
    while(x)
    {
        cnt ++;
        x &= (x - 1);
    }
    return cnt;
}

int main()
{
  //  freopen("in.txt", "r", stdin);
    char c;
    while(~scanf("%d%d", &n, &m))
    {
        memset(dp, -1, sizeof(dp));
        init();
        //输入,同时记录该行的状态
        for(int i = 1; i <= n; i ++)
        {
            cur[i] = 0;
            for(int j = 1; j <= m; j ++)
            {
                cin >> c;
                if(c == 'H') cur[i] += (1 << (m - j));
            }
        }
        //初始化第一行
        for(int i = 1; i <= top; i ++)
        {
            num[i] = jcount(state[i]);
            if(fit(state[i], cur[1])) dp[1][1][i] = num[i];
        }
        //分层dp
        for(int i = 2; i <= n; i ++)
        {
            for(int t = 1; t <= top; t ++)//当前行
            {
                if(!fit(state[t], cur[i])) continue;
                for(int j = 1; j <= top; j ++)//上上行
                {
                    if(!fit(state[j], cur[i - 2])) continue;
                    if(state[t] & state[j]) continue;
                    for(int k = 1; k <= top; k ++) //上一行
                    {
                        if(!fit(state[k], cur[i - 1])) continue;
                        if(state[t] & state[k]) continue;
                        if(dp[i - 1][j][k] == -1) continue;
                        dp[i][k][t] = max(dp[i][k][t], dp[i - 1][j][k] + num[t]);
                    }
                }
            }
        }
        //输出
        int ans = 0;
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= top; j ++)
                for(int k = 1; k <= top; k ++)
                    ans = max(ans, dp[i][j][k]);
        printf("%d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值