题意:略。
代码中有些简短注释。
#include <iostream>
using namespace std;
int dp[105][65][65]; //dp[i][k][j]存储的值为当第i-1行为k状态,第i行为j状态时可放炮兵的最大数量
int state[65], sum[65];
int map[105];
int cnt, row, col;
#define max(a, b) a>b?a:b
bool judge(int stt)
{
if(stt & (stt<<1)) return false; //若一个放炮兵的情况的二进制序列中相邻的位同时为1,则不合理
if(stt & (stt<<2)) return false; //若一个放炮兵的情况的二进制序列中相隔1位的位同时为1,则不合理
return true;
}
int getSum(int stt)
{
int sum = 0;
for(int i = 0; i < col; i++)
if((1<<i)&stt) sum++; //计算某一种情况的二进制序列中1的个数,即放的炮兵的人数
return sum;
}
void makeState()
{
for(int i = 0; i < (1<<col); i++) //每一个i的二进制序列即为一个放炮兵的情况,1为放炮兵,0为不放
if(judge(i)) //判断每一个放炮兵的情况是否合理
{
state[cnt] = i;
sum[cnt++] = getSum(i);
}
}
int main()
{
int i, j, k, l;
char ch;
memset(map, 0, sizeof(map));
scanf("%d%d", &row, &col);
memset(dp, -1, sizeof(dp));
getchar();
for(i = 0; i < row; i++)
{
for(j = 0; j < col; j++)
{
scanf("%c", &ch);
if(ch == 'H')
map[i] |= (1<<j); // map存储每一行的状态,即将P当成0,H当成1进行二进制存储
}
getchar(); //因为getchar又调试了好久。。。
}
cnt = 0;
makeState();
for(i = 0; i < cnt; i++) //矩阵第1行特殊处理
if(!(state[i]&map[0]))
dp[0][0][i] = sum[i];
int ans = 0, tmp;
for(i = 1; i < row; i++) //从第二行开始遍历
for(j = 0; j < cnt; j++) //遍历所有放炮兵的合理的状态
{
if(state[j]&map[i]) continue; //若第i行不可能取到第j种放炮兵的状态,j++
for(k = 0; k < cnt; k++) //遍历第i-1行的状态
{
if(state[j]&state[k]) continue; //若i-1行与i行有炮兵互相攻击,k++
for(l = 0; l < cnt; l++) //遍历第i-2行的状态
{
if(state[j]&state[l] || dp[i-1][l][k] == -1) continue;
/*dp[i-1][l][k] == -1, 用于判断i-2行的l状态和i-1行的k状态
对于map[i-2]和map[i-1]的情况是否是可以取到的,若dp[i-1][l][k] == -1,
说明是目前l,k两种状态时不可能取到的(表达得不是很清楚,我悲催的语言表达)
*/
tmp = dp[i-1][l][k]+sum[j];
if(dp[i][k][j] < tmp)
dp[i][k][j] = tmp;
}
}
}
//这样写可以处理边界问题,我曾经想就在上面的四重循环中记录
// 最大值来得到ans,但是当row为1时,不会进行上面的四重循环
for(i = 0; i < cnt; i++)
for(j = 0; j < cnt; j++)
if(ans < dp[row-1][i][j])
ans = dp[row-1][i][j];
printf("%d\n", ans);
//system("pause");
return 0;
}