题意:司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
题解:第 r 行与第 r - 3 行互相不干扰。dp[r][j][i] = max { dp[r-1][k][j] + num[i], dp[r][j][i] }。 dp[r][j][i]表示第r行的状态为i,第r-1行的状态为j。
#include <iostream>
using namespace std;
#define M 105
int dp[M][M][M], row[M];
int num[M], s[M];
int R, C, cnt, ans;
inline bool judgeRow ( int r, int id ) /* 判断在第r行,第id个状态是否合法 */
{
if ( (s[id] & row[r]) != s[id] )
return false;
if ( (s[id] & (s[id]<<1)) || (s[id] & (s[id]<<2)) )
return false;
return true;
}
void initial ( int Max )
{
int i, j;
ans = cnt = 0;
for ( i = 0; i <= Max; i++ )
{
if ( (i & (i<<1)) || (i & (i<<2)) )
continue;
for ( j = 0; j < C; j++ )
if ( i & ( 1 << j ) )
num[cnt]++; /* 第cnt个状态摆放了num[cnt]只部队 */
s[cnt++] = i;
}
for ( i = 0; i < cnt; i++ ) /* 初始化第一行 */
if ( judgeRow ( 1, i ) )
{
dp[1][0][i] = num[i];
if ( num[i] > ans ) ans = num[i];
}
}
int main()
{
char str[12];
int i, j, k, r, temp;
memset(row,0,sizeof(row));
memset(dp,0,sizeof(dp));
memset(num,0,sizeof(num));
scanf("%d %d",&R,&C); getchar();
for ( i = 1; i <= R; i++ )
{
scanf("%s",str);
for ( j = 0; j < C; j++ )
if ( str[j] == 'P' )
row[i] |= (1<<j);
}
initial( (1<<C)-1 );
for ( r = 2; r <= R; r++ )
{
for ( i = 0; i < cnt; i++ )
{
if ( ! judgeRow(r, i) )
continue;
for ( j = 0; j < cnt; j++ )
{
if ( ! judgeRow (r-1, j) || (s[j] & s[i]) )
continue;
for ( k = 0; k < cnt; k++ )
{
if ( ! judgeRow(r-2,k) || (s[k] & s[j]) || (s[k] & s[i]) )
continue;
temp = dp[r-1][k][j] + num[i];
if ( temp > dp[r][j][i] ) dp[r][j][i] = temp;
if ( temp > ans ) ans = temp;
}
}
}
}
printf("%d\n",ans);
return 0;
}
下面是错误的代码。
/* 忽略了dp[i][j]可能有多个相等的最大值,那么状态j可能有多种前驱 */
#include <iostream>
using namespace std;
#define M 105
#define N (1<<11)
int dp[M][N], row[M];
int num[N], pre[M][N];
int R, C, State;
bool isLeagal ( int x, int y, int r )
{
if ( (x & y) || (pre[r-1][x] & y) || (y & row[r]) != y )
return false;
if ( (x & (x<<1)) || (x & (x<<2)) || (y & (y<<1)) || (y & (y<<2)) )
return false;
return true;
}
void initial ()
{
int i, j;
memset(dp,0,sizeof(dp));
memset(pre,0,sizeof(pre));
memset(num,0,sizeof(num));
for ( i = 0; i <= State; i++ )
for ( j = 0; j < C; j++ )
if ( i & ( 1 << j ) )
num[i]++;
for ( i = 0; i <= State; i++ )
if ( (i & row[1]) == i && !(i & (i<<1)) && !(i & (i<<2)) )
dp[1][i] = num[i];
}
int main()
{
//freopen("a.txt","r",stdin);
char str[12];
int i, j, k;
scanf("%d %d",&R,&C); getchar();
memset(row,0,sizeof(row));
for ( i = 1; i <= R; i++ )
{
scanf("%s",str);
for ( j = 0; j < C; j++ )
if ( str[j] == 'P' )
row[i] |= (1<<j);
}
State = (1<<C);
initial();
for ( i = 2; i <= R; i++ )
{
for ( j = 0; j <= State; j++ )
{
for ( k = 0; k <= State; k++ )
{
if ( isLeagal ( k, j, i ) )
{
if ( dp[i-1][k] + num[j] > dp[i][j] )
{
dp[i][j] = dp[i-1][k] + num[j];
pre[i][j] = k; /* 忽略了dp[i][j]可能有多个相等的最大值,那么状态j可能有多种前驱 */
}
}
}
}
}
int ans = 0;
for ( i = 0; i <= State; i++ )
if ( dp[R][i] > ans )
ans = dp[R][i];
printf("%d\n",ans);
return 0;
}