292. 炮兵阵地(状压DP棋盘式&滚动数组&三排关系)

题目:https://www.acwing.com/problem/content/294/

题意:给一个包含H和P的网格图,P代表可以放东西,H代表不能放东西,并且俩个东西不能互相攻击到,攻击范围如下图,问最多能够放多少东西。
在这里插入图片描述
题解:状压DP,这里要开三维,f[i][j][k],第一维是题目给的n,第二维代表上一层的所有状态,第三维代表本层的所有状态,这样就可以表示了,这样开三维空间,会炸内存,所以第一维用滚动数组(写法和普通数组一样,只是用数组的时候第一维&1就ok了)

y总讲解:
在这里插入图片描述

#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
//#include <unordered_map>
//#include <unordered_set>
//#include <bits/stdc++.h>
//#define int long long
#define pb push_back
#define PII pair<int, int>
#define mpr make_pair
#define ms(a, b) memset((a), (b), sizeof(a))
#define x first
#define y second
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 102, M = 1 << 10;
using namespace std;

vector<int> vec;
int f[2][M][M];  //滚动数组,第二维是上一层的状态,第三层是本层的状态
int ma[N], cnt[N];  // ma存题目给的图,cnt存这个状态放了炮的数量
int n, m;
int check(int num) {
    for (int i = 0; i < m; i++) {
        if ((num >> i & 1) && (num >> i + 1 & 1)) return 0;
        if ((num >> i & 1) && (num >> i + 2 & 1)) return 0;
    }
    return 1;
}
int get1(int num) {
    int s = 0;
    while (num) {
        s += num % 2;
        num = num / 2;
    }
    return s;
}
signed main(int argc, char const *argv[]) {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        string s;
        cin >> s;
        int ant = 0;
        for (int j = 0; j < s.size(); j++)
            if (s[j] == 'H') ant += 1 << j;
        ma[i] = ant;
    }
    for (int i = 0; i < 1 << m; i++)
        if (check(i)) vec.emplace_back(i), cnt[i] = get1(i);
    int len = vec.size();
    int maxx = 0;
    for (int i = 1; i <= n; i++) {  //枚举层数
        for (int j = 0; j < len; j++) {  //枚举上一层的状态
            for (int k = 0; k < len; k++) {//枚举本层的状态
                for (int u = 0; u < len; u++) {//枚举上上层的状态
                    int a = vec[j], b = vec[k], c = vec[u];//a代表上一层状态,b代表本层状态,c代表上上层状态
                    if ((a & b) == 0 && (a & c) == 0 && (b & c) == 0 &&
                        (a & ma[i - 1]) == 0 && (b & ma[i]) == 0) {//保证任何俩个状态不冲突,且不与题目给的图冲突,注意&运算优先级比==小
                        f[i & 1][j][k] =
                            max(f[i & 1][j][k], f[i - 1 & 1][u][j] + cnt[b]);//找上一层j状态下的再上一层u状态,这里用下标了,用状态来存也行,但注意其它有些地方也要变
                        // maxx = max(f[i][j][k], maxx);
                        // f[i][a][b]=max(f[i-1][0][c]+1,f[i][a][b]);
                        maxx = max(f[i & 1][j][k], maxx);  //取最大
                    }
                }
            }
        }
    }
        //也可以最后暴力找一遍
    // maxx = 0;
    // for (int j = 0; j < len; j++) {  
    //     for (int k = 0; k < len; k++) {
    //         // printf("%d ", f[n][j][k]);
    //         maxx = max(f[n & 1][j][k], maxx);
    //     }
    // }
    cout << maxx << endl;

    //也可以在上面第一层for循环的时候<=n+2,多来两层,直接输出这个答案
    // cout << f[n + 2 & 1][0][0] << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值