題目#
[NOI2001] 炮兵陣地#
司令部的將軍們打算在 $N\times M$ 的網格地圖上部署他們的炮兵部隊。
一個 $N\times M$ 的地圖由 $N$ 行 $M$ 列組成,地圖的每一格可能是山地(用 $\texttt {H}$ 表示),也可能是平原(用 $\texttt {P}$ 表示),如下圖。
在每一格平原地形上最多可以布置一支炮兵部隊(山地上不能夠部署炮兵部隊);一支炮兵部隊在地圖上的攻擊範圍如圖中黑色區域所示:
如果在地圖中的灰色所標識的平原上部署一支炮兵部隊,則圖中的黑色的網格表示它能夠攻擊到的區域:沿橫向左右各兩格,沿縱向上下各兩格。
圖上其它白色網格均攻擊不到。從圖上可見炮兵的攻擊範圍不受地形的影響。
現在,將軍們規劃如何部署炮兵部隊,在防止誤傷的前提下(保證任何兩支炮兵部隊之間不能互相攻擊,即任何一支炮兵部隊都不在其他支炮兵部隊的攻擊範圍內),在整個地圖區域內最多能夠擺放多少我軍的炮兵部隊。
對於 $100%$ 的數據,$N\le 100$,$M\le 10$,保證字符僅包含 P
與 H
。
題解#
首先看到數據範圍,直接狀態壓縮 dp
考慮設計狀態
先嘗試f[i][j]
表示推到第 i 行,且第 i 行的狀態的二進制為 j 的,能放下的最多的炮兵部隊
嘗試轉移
f [i][j] 要從 f [i-1][~] 轉移過來,但 f [i-1][~] 的狀態包含的集合又不是都能用,所以沒法轉移
考慮加狀態
f[i][j][fl]
表示推到第 i 行,且第 i 行的狀態的二進制為 j 的,第 i-1 行的狀態為 fl 的,能放下的最多的炮兵部隊
可以轉移了
實現#
判斷是否能放,二進制和地圖與一下,二進制本身分別和其左移一位、兩位與一下
其餘同理
出現問題#
1. 狀壓 dp 最好下標從 0 開始
2. 學習了滾動數組
代碼#
#include<bits/stdc++.h>
using namespace std;
int read()
{
int f = 1, x = 0;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return f * x;
}
int n, m;
int Map[110];//如果是1表示山地,0表示平原
int f[3][1 << 10][1 << 10]; // i,當前狀態,上一行狀態
int num_of_1[1 << 10];
int getsum(int x)
{
int ans = 0;
while(x)
{
x &= (x - 1);
ans++;
}
return ans;
}
int main()
{
for(int i = 0;i < (1 << 10);i++)
{
num_of_1[i] = getsum(i);
}
n = read(), m = read();
char ch;
for(int i = 0;i < n;i++)
{
for(int j = 0;j < m;j++)
{
cin >> ch;
if(ch == 'H')
{
Map[i] |= (1 << j);
}
}
}
for(int j = 0;j < (1 << m);j++)
{
if(j & (j << 1) || j & (j << 2) || j & Map[0]) continue;
f[0][j][0] = num_of_1[j];
}
for(int j = 0;j < (1 << m);j++)
{
for(int fl = 0;fl < (1 << m);fl++)
{
if(j & (j << 1) || j & (j << 2) || j & Map[1] || fl & (fl << 1) || fl & (fl << 2) || fl & Map[0] || j & fl || j & Map[1])
continue;
f[1][j][fl] = num_of_1[j] + num_of_1[fl];
}
}
for(int i = 2;i < n;i++)
{
for(int j = 0;j <= (1 << m) - 1;j++)//當前行
{
if(j & Map[i] || j & (j << 1) || j & (j << 2))
{
continue;
}
for(int fl = 0; fl <= (1 << m) - 1;fl++)//上一行
{
if(fl & Map[i - 1] || fl & (fl << 1) || fl & (fl << 2) || j & fl)
{
continue;
}
for(int fll = 0; fll < (1 << m);fll++)
{
if(fll & Map[i - 2] || fll & (fll << 1) || fll & (fll << 2) || j & fll || fl & fll)
{
continue;
}
f[i % 3][j][fl] = max(f[i % 3][j][fl], f[(i - 1) % 3][fl][fll] + num_of_1[j]);
}
}
}
}
int ans = 0;
for(int j = 0;j < (1 << m);j++)
{
for(int fl = 0;fl < (1 << m);fl++)
{
ans = max(ans, f[(n - 1) % 3][j][fl]);
}
}
cout << ans << endl;
return 0;
}