P2704 [NOI2001]炮兵阵地
只能说还是不会状压dp。。
这道题看上去很熟悉,就是n国王问题,只不过多了一些限制。
题目还会给你一个地图,告诉你那些地方不能放国王,那么用二进制存下来这个状态,判断的时候&一下即可。
这道题的国王的手变长了,是上下左右各两格,所以我们在定义状态的时候应该注意,在判断不可能的解的时候要左移和右移两位&一&。
定义状态我本来想定义四维的,但是发现你会爆空间。但是放国王要考虑到当前行和上面两行的啊!
其实最上面一行是可以被滚掉的,你不定义也可以,不信的话看代码。
其他的就没有什么大问题了。
代码:
#include<cstdio>
#include<algorithm>
const int maxn = 102, maxm = 12, maxM = 1048;
int stdd[maxn];
int status[maxM], tot;
long long dp[maxn][maxM][maxM];
int n, m;
int all;
#define lowbit(x) (x & -x)
int getsum(int x)
{
int ans = 0;
while(x)
{
x -= lowbit(x); ans++;
}
return ans;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
char ch[maxm]; scanf("%s", ch);
for(int j = 0; j < m; j++)
{
stdd[i] <<= 1;
if(ch[j] == 'H') stdd[i] |= 1;
}
}
all = (1 << m) - 1;
for(int i = 0; i <= all; i++)
{
if(i & (i << 1)) continue;
if(i & (i << 2)) continue;
if(i & (i >> 1)) continue;
if(i & (i >> 2)) continue;
status[++tot] = i;
if(!(i & stdd[1])) dp[1][i][0] = getsum(i);
}
for(int i = 1; i <= tot; i++)// 2nd row
{
if(stdd[2] & status[i]) continue;
for(int j = 1; j <= tot; j++)// 1st row
{
if(stdd[1] & status[j]) continue;
if(status[i] & status[j]) continue;
dp[2][status[i]][status[j]] = std::max(dp[2][status[i]][status[j]], dp[1][status[j]][0] + getsum(status[i]));
}
}
for(int i = 3; i <= n; i++)
{
for(int j = 1; j <= tot; j++)// ith row
{
if(stdd[i] & status[j]) continue;
for(int k = 1; k <= tot; k++)// i-1th row
{
if(stdd[i - 1] & status[k]) continue;
if(status[j] & status[k]) continue;
for(int l = 1; l <= tot; l++)// i-2th row
{
if(stdd[i - 2] & status[l]) continue;
if(status[l] & status[j]) continue;
if(status[l] & status[k]) continue;
dp[i][status[j]][status[k]] = std::max(dp[i][status[j]][status[k]], dp[i - 1][status[k]][status[l]] + getsum(status[j]));
}
}
}
}
long long ans = 0;
for(int i = 1; i <= tot; i++)
for(int j = 1; j <= tot; j++)
ans = std::max(ans, dp[n][status[i]][status[j]]);
printf("%lld\n", ans);
}