pku 1185 炮兵阵地 解题报告
思路:用二进制保存这一行中炮兵,例如1 0 0 0, 1表示炮兵.那么很明显用到了压缩dp。认真发现炮兵摆放的规律可知,每3行进行一个判断就可以了.也就是说,我们可以每3行都进行dp的转移,当然1、2行有特殊的处理.比如第i行为:1 0 0 0 0 第i+1行为0 0 0 0 1,那么怎么知道i+2的状态呢?其实我们可以先将合法的状态枚举出来,这样问题就变得简单,我们枚举i+2d状态时就与i行、i+1行的状态进行比较.看看这个是否合法,当然i行与i+1行合法,但不一定与i+2合法,这时我们就必须用dp[i][j][k]来进行每3行的dp了。假设当前行i,i-1行的状态为j,i+2行的状态为k.
算法:
1、 用map[i]保存i行存在山地的二进制状态;
2、 枚举一行(假设没有山地,也就是i行的枚举状态)的合法状态;
3、 特殊处理1与2行的情况;
4、 枚举判断当前行i的合法状态与map[i]的状态是否合法;再枚举i-1行,i-2行这3行之间的合法状态.
5、 合法的话,进行dp转移:
dp[i][j][k] = max(dp[i][j][k],dp[i - 1][k][0] + num[j]);
AC代码:
#include <stdio.h>
#include <string.h>
#define N 101
#define M 11
#define max(a, b) (a > b ? a : b)
//dp[i][j][k]表示在第i行的合法状态为j,第i-1行的合法状态为k时,炮兵的数量
int dp[N][61][61];
int num[N], map[N], state[N], n, m, count;
void init()
{
int i, temp;
count = 0;
//枚举一行中都可能出现的状态,从0到2^m - 1,判断其是否合法
//此函数的统计是假设当该行都为平地时,即共有cnt+1个合法状态
for (i = 0; i < (1 << m); i++)
{
temp = i;
if (((temp << 1) & i) | ((temp << 2) & i))
{
continue; //判断该行在这个状态时是否合法(任意炮兵都不在其他炮兵的攻击范围之内)
}
state[count] = i; //通过数组state[]记录合法的状态(十进制表示)
num[count] = temp & 1; //num[]数组记录这个合法状态下'1'的个数(也就是炮兵的个数)
while (temp = (temp >> 1))
{
num[count] += temp & 1;
}
count++;
}
}
void solve()
{
int i, j, k, p;
for (i = 0; i < n; i++)
{
for (j = 0; j < count; j++)
{
//由于state[]记录的是一行的合法状态,那么用map[]&state[],则可以判断map[i]与state[i]是合法的
if (map[i] & state[j])
{
continue;
}
//i=0与i=1为特殊处理
if (i == 0)
{
dp[i][j][0] = num[j];
}
else if (i == 1)
{
for (k = 0; k < count; k++) //枚举第0行的可能状态
{
if (state[j] & state[k])
{
continue; //判断上下两行(0和1)的合法状态是否兼容彼此
}
dp[i][j][k] = max(dp[i][j][k], dp[i-1][k][0]+num[j]);
}
}
else
{
//枚举第i行的所有可能状态
for (k = 0; k < count; k++)
{
//判断上下两行(i和i-1)的合法状态是否兼容彼此
if (state[j] & state[k])
{
continue;
}
for (p = 0; p < count; p++)
{
//判断分别i行与i-2行的合法状态、i-1行与i-2行都是否彼此兼容
if (state[k] & state[p] || state[j] & state[p])
{
continue;
}
dp[i][j][k] = max(dp[i][j][k], dp[i-1][k][p]+num[j]);
}
}
}
}
}
int ans = 0;
for (j = 0; j < count; j++)
{
for (k = 0; k < count; k++)
{
if (dp[n - 1][j][k] > ans)
{
ans = dp[n - 1][j][k];
}
}
}
printf("%d/n", ans);
}
int main()
{
freopen("1.txt", "r", stdin);
int i, j;
char s[11];
scanf("%d%d", &n, &m);
for (i = 0; i < n; i++)
{
scanf("%s", s);
//将每一行出现的山地存储在二进制中
for (j = 0; j < m; j++)
{
if (s[j] == 'H')
{
map[i] += (1 << (m - 1 - j));
}
}
}
init();
solve();
return 0;
}