P2704 [NOI2001] 炮兵阵地
H
I
N
T
HINT
HINT
对于 100% 的数据,
N
≤
100
N≤100
N≤100,
M
≤
10
M≤10
M≤10,保证字符仅包含
p
p
p 与
h
h
h。
M的数据范围很小,考虑状压DP。
首先,影响第 i i i行的有 i − 1 i-1 i−1行和 i − 2 i-2 i−2行,所以我们的 d p dp dp数组要体现出来这两个数据的,同时体现出来自己现在在哪行。
所以我们可以开个三维的
d
p
dp
dp数组:
d
p
[
p
r
e
]
[
n
o
w
]
[
i
]
dp[pre][now][i]
dp[pre][now][i],其中的i表示当前正在处理第
i
i
i行,第
i
i
i行的状态集合为
n
o
w
now
now,
i
−
1
i-1
i−1行的状态集合为
p
r
e
pre
pre。
为什么我们不开
d
p
[
p
p
r
e
]
[
p
r
e
]
[
i
]
dp[ppre][pre][i]
dp[ppre][pre][i]呢 (我一开始是这样想的,后来在转移的时候发现不对) ,因为我们的第
i
i
i行的状态虽然受前两行影响,但当我们由
i
−
1
i-1
i−1转移来的时候就会经历
d
p
[
p
p
r
e
]
[
p
r
e
]
[
i
−
1
]
dp[ppre][pre][i-1]
dp[ppre][pre][i−1],也就体现了前两行满足了要求才能转移到现在的
n
o
w
now
now。
其次我们看一下开的数组的大小。所有的集合的数量应该是
1
<
<
m
1<<m
1<<m,那么我们要开的数组大小就是
1024
∗
1024
∗
100
1024*1024*100
1024∗1024∗100会MLE,此时有两个解决方法:
Ⅰ滚动数组
Ⅱ由于不是所有的1024个都符合题中的摆放条件,所以我们只用处理出符合条件的那些,离散化一下就行了。
下文用的是第Ⅱ种方法
有的题解里面是处理出了所有①不考虑地图的符合条件的集合;
我在下面是处理出了②符合每一行地图的,并分别存起来。
如果是第①种,那么在后续的状态转移中还要多进行一次判断。
预处理的过程中,每个炮兵左右两个格子内不能有炮兵,我们让二进制中1代表有炮兵,0代表无,那么 ( s < < 2 ) (s<<2) (s<<2)就代表把整行向左移动了两位,如果它和本来的 s s s按位与后结果为不为0,那么就代表有俩棋子重了,就不行。其他的情况和这一样判断。
而判断一个行的集合是否和上两行的集合有某一列上冲突了,就直接把他们按位与再判断就行了。
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll unsigned long long
#define P pair <int,int>
//#define int ll
using namespace std;
ll mod = 1e9 + 7;
inline ll qpow(ll a, ll b) { ll ret = 1; while (b) { if (b & 1)ret = ret * a % mod; a = a * a % mod; b >>= 1; }return ret; }
inline int read() { int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }while ('0' <= ch && ch <= '9') { x = x * 10; x += ch - '0'; ch = getchar(); }return x * f; }
const int maxn = 110;
const int maxm = 12;
const int inf = 0x3f3f3f3f;
char mp[maxn][maxm];
struct node
{
int data, num;
};
vector<node>G[maxn];
int n, m;
bool check(int s, int line)
{
for (int i = 1;i <= m;i++)
{
if ((mp[line][i] == 'H') && (s & (1 << (i - 1))))
return false;
}
return true;
}
int get_cnt(int x)
{
int ret = 0;
while (x)
{
if (x & 1)
ret++;
x /= 2;
}
return ret;
}
int dp[100][100][maxn];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1;i <= n;i++)
{
scanf("%s", mp[i] + 1);
}
for (int s = 0;s <= ((1 << m) - 1);s++)
{
if ((s << 1) & s) continue;
if ((s << 2) & s) continue;
if ((s >> 1) & s) continue;
if ((s >> 2) & s) continue;
int cnt = get_cnt(s);
for (int i = 1;i <= n;i++)
{
if (check(s, i))
{
G[i].push_back({ s,cnt });
}
}
}
mem(dp, -inf);
//dp[pre][now][i]
for (int i = 0;i < G[1].size();i++)
{
dp[0][i][1] = G[1][i].num;
}
for (int i = 0;i < G[2].size();i++)
{
for (int j = 0;j < G[1].size();j++)
{
if (G[1][j].data & G[2][i].data)
continue;
dp[j][i][2] = max(dp[j][i][2], dp[0][j][1] + G[2][i].num);
}
}
int ans=0;
for (int i = 3;i <= n;i++)
{
for (int j = 0;j < G[i].size();j++)
{
for (int k = 0;k < G[i - 1].size();k++)
{
if (G[i][j].data & G[i - 1][k].data) continue;
for (int l = 0;l < G[i - 2].size();l++)
{
if (G[i - 1][k].data & G[i - 2][l].data) continue;
if (G[i][j].data & G[i - 2][l].data) continue;
dp[k][j][i] = max(dp[k][j][i], dp[l][k][i - 1] + G[i][j].num);
ans=max(ans,dp[k][j][i]);
}
}
}
}
printf("%d\n", ans);
}