POJ 1185 炮兵阵地 状态压缩(DP)

题意:司令部的将军们打算在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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值