NOI2001 炮兵阵地 - 状压DP

49 篇文章 0 订阅
6 篇文章 0 订阅

状压DP往往有套路,问题划分为阶段,并且阶段不是“一个”,而是“一群”,在上一个阶段到下一个阶段时,会增加一堆数据而不是一个,然后这一堆数据可以压缩成一个二进制,表示哪些有哪些没有
阶段的划分要“整齐”,比如这题是一行一行来的,维度的设计要考虑影响关系,因此设f[i][j][k]表示阶段进行到第i行(前i行)最后一行状态为k,最后一行的上一行状态为j的最优解
另一个套路就是预处理,几乎状态题都要预处理出“可转移”的各种情况
大致形式是:i, j 表示两个二进制串 则g[i][j] 表示i, j若处于相邻阶段,或者说仅仅是“若i状态转移到j状态”,会消耗或为答案贡献什么

#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int INF = (1<<30);
int gra[110][12],n,m,f[110][100][100],s[110],tot,ans,temte[12],temtot;
void print(int k);
void initialization() {
    for(int i=0; i<1<<m; i++) {
        int pre=-1;
        bool flg = true;
        for(int j=0; j<m; j++) 
            if((i>>j) & 1) {
                if(pre>=0 && j-pre<3) { //注意pre>=0,因为最右边第一位可能就是1
                    flg = false;
                    break;
                }
                pre = j;
            }
        if(flg) 
            s[++tot] = i;
    }
    for(int i=1; i<=n; i++) 
        for(int j=1; j<=m; j++) 
            for(int l=1; l<=m; l++) 
                f[i][j][l] = -INF;
}
bool dep(int p, int k) {
    int tem = s[k];
    for(int i=0; i<m; i++) 
        if(!gra[p][m-i] && (tem >> i & 1)) 
            return false;
    return true;
}

void print(int k) {//调试用,输出二进制
     k = s[k];
     temtot = 0;
     for(int i=0; i<m; i++) 
        temte[++temtot] = (k >> i) & 1;
     for(int i=temtot; i>=1; i--) 
        cout <<temte[i];
     cout << endl;
}

int count(int k) {
    int tem = s[k];
    int sum = 0;
    for(int i=0; i<m; i++) 
        if(tem>>i&1) sum++;
    return sum;
}
int main() {
    scanf("%d %d", &n, &m);
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) {
            char ch;
            cin >> ch;
            if(ch == 'P') gra[i][j] = 1;
        }
    }
    initialization();
    f[0][0][0] = 0;
    for(int i=1; i<=n; i++) 
        for(int j=1; j<=tot; j++) 
            for(int k=1; k<=tot; k++) {
                int temp = -INF;
                if(!(s[j] & s[k]) && dep(i,j) && dep(i-1,k) ) {
                    for(int l=1; l<=tot; l++) 
                        if(!(s[j] & s[l])) 
                            temp = max(temp, f[i-1][k][l]);
                    f[i][j][k] = temp + count(j);
                }
            }
    for(int i=1; i<=tot; i++)
        for(int j=1; j<=tot; j++) 
            if(!(s[i] & s[j]) && dep(n,i) && dep(n-1,j))
                ans = max(ans, f[n][i][j]);
    printf("%d\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值