POJ 1185 炮兵阵地 ( dp[状态压缩] )

7 篇文章 0 订阅

题意是汉语.

解析:可以发现,对于每一行放大炮的状态,只与它上面一行和上上一行的状态有关,每一行用状态压缩的表示方法,0表示不放大炮,1表示放大炮,同样的,先要满足硬件条件,即有的地方不能放大炮,然后就是每一行中不能有两个1的距离小于2(保证横着不互相攻击),这些要预先处理一下。然后就是状态表示和转移的问题了,因为是和前两行的状态有关,所以要开个三维的数组来表示状态,当前行的状态可由前两行的状态转移而来。即如果当前行的状态符合前两行的约束条件(不和前两行的大炮互相攻击),则当前行的最大值就是上一个状态的值加上当前状态中1的个数(当前行放大炮的个数) 

状态表示dp[i][j][k] 表示第i行状态为j,第i-1状态为k时的最大炮兵个数。 

状态转移方程dp[i][j][k] =max(dp[i][j][k],dp[i-1][j][h]+num[j]); num[j]为t状态中1的个数 .


//C++	1868K	282MS
#include <stdio.h>
#include <string.h>
#define MAX(A,B) ((A)>(B)?(A):(B))
#define V 65	// V 可搜索得出,当M=10时,共有60种情况

int dp[105][V][V] ;

int nState ;
int state[V] , num[V] ;

void 
Init ( int const m ) 
{
	int k ;
	k = 1 << m ;
	nState = 0 ;
	for ( int i = 0 ; i < k ; i ++ ) 
	{
		if ( 0 == ( i &( i << 1 ) ) && 0 == ( i & ( i << 2 ) ) ) 
		{
			state[nState] = i ;
			num[nState] = 0 ;
			int j = i ;
			while ( j ) 
			{
				num[nState] += j % 2 ;
				j >>= 1 ;
			}
			nState ++ ;
		}
	}
	return ;
}

int 
main ( ) 
{
	int n , m ;  //n 代表行,m代表列
	int row[105] ;
	char str[15] ;
	while ( EOF != scanf ("%d%d" , & n , & m ) ) 
	{
		Init ( m ) ;
		for ( int i = 0 ; i < n ; i ++ ) 
		{
			row[i] = 0 ;
			scanf ("%s" , str ) ;
			for ( int j = 0 ; j < m ; j ++ ) 
			{
				if ( 'P' == str[j] ) 
				{
					row[i] += 1 << j ;
				}
			}
		}
		memset ( dp , 0 , sizeof ( dp ) ) ;

		//计算dp[0] 
		for ( int j = 0 ; j < nState ; j ++ ) 
		{
			if ( ( state[j] & row[0] ) != state[j] ) 
			{
				continue ;
			}
			/*
			for ( int k = 0 ; k < nState ; k ++ ) 
			{
				dp[0][j][k] = num[j] ;
			}
			*/
			dp[0][j][0] = num[j] ;
		}

		//计算dp[1] 
		/*
		if ( n > 1 )
		{
			for ( int j = 0 ; j < nState ; j ++ ) 
			{
				if ( ( state[j] & row[1] ) != state[j] ) 
				{
					continue ;
				}
				for ( int k = 0 ; k < nState ; k ++ ) 
				{
					if ( 0 == ( state[j] & state[k] ) ) 
					{
						dp[1][j][k] = dp[0][k][0] + num[j] ;
					}
				}
			}
		}
		*/

		//计算dp[1~n-1] 
		for ( int i = 1 ; i < n ; i ++ ) 
		{
			for ( int j = 0 ; j < nState ; j ++ ) 
			{
				if(  ( row[i] & state[j] ) != state[j] )	//如果状态j和第i行的地形冲突,注意,有state[j] = 0 这种情况
				{
					continue ;
				}
				for ( int k = 0 ; k < nState ; k ++ ) 
				{
					if ( state[j] & state[k] )			//如果第i行状态j和第i-1行状态k冲突
					{
						continue ;
					}
					for ( int h = 0 ; h < nState ; h ++ ) 
					{
						if ( state[j] & state[h] )		//如果第i行状态j和第i-2行状态h冲突
						{
							continue ;
						}
						dp[i][j][k] = MAX ( dp[i][j][k] , dp[i-1][k][h] + num[j] ) ;
					}
				}
			}
		}

		int ans ;
		ans = 0 ;
		for ( int j = 0 ; j < nState ; j ++ ) 
		{
			for ( int k = 0 ; k < nState ; k ++ ) 
			{
				ans = MAX ( ans , dp[n-1][j][k] ) ;
			}
		}
		
		printf ("%d\n" , ans ) ;
	}
	return 0 ;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值