洛谷题单指南-状态压缩动态规划-P2704 [NOI2001] 炮兵阵地
原题链接:https://www.luogu.com.cn/problem/P2704
题意解读:一个炮兵部队可以覆盖横向纵向两行的位置,给定n*m区域,可以部署炮兵为P,不能部署炮兵为H,求不互相攻击的情况下最多可以部署多少炮兵部队。
解题思路:
通过对一行是否部署炮兵部署,可以使用二进制整数来压缩状态。
由于当前行的状态,与前两行的状态有关,状态可以如此定义:
设f[i][j][k]表示前i行已经部署完,第i行状态是j,第i-1行状态是k的最多炮兵部队数
使用mask[]来保存每一行每个位置是否可以部署炮兵的情况:
for(int i = 1; i <= n; i++)
{
for(int j = 0; j < m; j++)
{
char x;
cin >> x;
if(x == 'H') mask[i] |= (1 << j); //将一行山地位置用二进制1表示
}
}
预处理所有本行内合法的状态,同时计算每个状态中1的个数,减少枚举范围:
for(int i = 0; i < 1 << m; i++)
{
if((i & i << 1) || (i & i << 2)) continue; //状态中存在11或者101
stat.push_back(i);
count(i);
}
下面进行初始化,主要针对第一行进行赋初值:
for(auto s : stat)
{
if(mask[1] & s) continue;
f[1][s][0] = cnt[s];
ans = max(ans, f[1][s][0]);
}
接下来,从第2行开始枚举所有行i,枚举第i、i-1、i-2行的状态j,k,l,实现状态转移:
f[i][j][k] = max(f[i][j][k], f[i - 1][k][l] + cnt[j])
更新答案:
ans = max(ans, f[i][j][k])
如果空间不够用,还可以将第一维用滚动数组优化,这里直接AC了,就省了这一步。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 105, M = 10;
int mask[N], cnt[1 << M];
int f[N][1 << M][1 << M]; //f[i][j][k]表示前i行已经部署完,第i行状态是j,第i-1行状态是k的最多炮兵部队数
vector<int> stat; //一行合法的状态
vector<int> mp[1 << M]; //合法的状态转移
int n, m, ans;
void count(int x)
{
int res = 0;
for(int i = 0; i < m; i++)
if(x >> i & 1) res++;
cnt[x] = res; //保存x二进制中1的个数
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
for(int j = 0; j < m; j++)
{
char x;
cin >> x;
if(x == 'H') mask[i] |= (1 << j); //将一行山地位置用二进制1表示
}
}
for(int i = 0; i < 1 << m; i++)
{
if((i & i << 1) || (i & i << 2)) continue; //状态中存在11或者101
stat.push_back(i);
count(i);
}
//初始化第一行
for(auto s : stat)
{
if(mask[1] & s) continue;
f[1][s][0] = cnt[s];
ans = max(ans, f[1][s][0]);
}
//dp过程
for(int i = 2; i <= n; i++) //从第2行开始枚举
{
for(auto j : stat) //第i行的状态
{
if(mask[i] & j) continue; //第i行状态不合法
for(auto k : stat) //第i - 1行的状态
{
if((mask[i - 1] & k) || k & j) continue; //第i - 1行状态不合法
for(auto l : stat) //第i - 2行的状态
{
if((mask[i - 2] & l) || l & k || l & j) continue; //第i - 2行状态不合法
f[i][j][k] = max(f[i][j][k], f[i - 1][k][l] + cnt[j]);
}
ans = max(ans, f[i][j][k]);
}
}
}
cout << ans;
return 0;
}