poj 1185 炮兵阵地

链接:http://poj.org/problem?id=1185

题意:题目中文


这是个状态压缩dp的题,先根据行的长度m遍历保存所有可能的布置炮兵的方案,同时记录该方案炮兵的数量。


因为竖着也不能让炮兵互相攻击到,而且如果从上往下遍历的话,第i行会和i-1行和i-2行都有制约关系,我第一次和第二次想的办法都陷到这个错误了,自己水平经验都不够,没办法提早发现在后效性上犯的错误,这样没办法遍历完所有可行的方案。如果你的方案中第i-1行和i-2行是和第i行分开判断,并且没办法确定第i-1行的上一行的状态,那么十有八九就犯了和我一样的错误。


所以就需要在dp的时候必须知道滴i-1行当前状态的上一行曾选择的状态,也就是说第i行和i-1行的状态同时保存,这样才能在下一行中运用


dp[ i ][ j ][ k ] :第i行的状态为j,上一行的状态为k,并保存最大炮兵数

设总共可能的状态为up个

状态转移方程为:

在第i行选择j状态保证和i-1行,i-2行状态的合法的前提下有

dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][0~up]+sum[j]),k=0~up


代码:

#include<stdio.h>
#include<string.h>
#define max(a,b) (a)>(b)?(a):(b)
int mp[105],dp[105][65][65];//dp[i][j][k],第i行状态是j,上一行的状态是k
int sum[65],sta[65];
int up;
int is_ok(int x)
{
	if(x&(x<<1))
		return 0;
	if(x&(x<<2))
		return 0;
	return 1;
}
int getsum(int x)
{
	int sum=0;
	while(x>0)
	{
		if(x&1)
			sum++;
		x>>=1;
	}
	return sum;
}
void ini(int m)
{
	int i;
	up=0;
	for(i=0;i<(1<<m);i++)
	{
		if(is_ok(i))
		{
			sta[up]=i;
			sum[up]=getsum(i);
			up++;
		}
	}
}
int check(int a,int b)
{
	return a&b;
}
int main()
{
	int n,m,i,j,k,l;
	char s[15];
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		ini(m);
		for(i=0;i<n;i++)
		{
			scanf("%s",s);
			int x=0;
			for(j=0;j<m;j++)
				if(s[j]=='H')
					x|=(1<<(m-j-1));
			mp[i]=x;
		}
		memset(dp,0,sizeof(dp));
		//处理第一行
		for(i=0;i<up;i++)
			if(!check(sta[i],mp[0]))
				dp[0][i][0]=sum[i];
		//处理第二行
		if(n>=2)
			for(i=0;i<up;i++)//第二行要放i状态
				if(!check(sta[i],mp[1]))
					for(k=0;k<up;k++)//检测上一行
						if(!check(sta[i],sta[k])&&(dp[0][k][0]+sum[i]>dp[1][i][k]))
							dp[1][i][k]=dp[0][k][0]+sum[i];
		for(i=2;i<n;i++)
			for(j=0;j<up;j++)//第i行要放j状态
				if(!check(sta[j],mp[i]))
					for(k=0;k<up;k++)//检测上一行
						if(!check(sta[j],sta[k]))
							for(l=0;l<up;l++)//上上行
								if(!check(sta[j],sta[l]))
									if(dp[i-1][k][l]+sum[j]>dp[i][j][k])
										dp[i][j][k]=dp[i-1][k][l]+sum[j];
		int ans=0;
		for(i=0;i<up;i++)
			for(j=0;j<up;j++)
				if(dp[n-1][i][j]>ans)
					ans=dp[n-1][i][j];
		printf("%d\n",ans);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值