P2704 [NOI2001]炮兵阵地(状压)

题目链接


woc,优先级真恶心,调了我半天

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio> 
using namespace std;
const int mod=1e8;
int n,m;
int sq[105][12];
int mp[105];
bool judge[105],can[105][105];
int legal[105],sum[105];
long long dp[105][105][105];
//dp[i][s][t],记录当前在第i行当前状态为legal[s],上一行状态为legal[t]
long long ans=0;
char a;
int cnt=0;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		cin>>a;
		if(a=='P') sq[i][j]=1;
		if(a=='H') sq[i][j]=0;
	 } 
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		mp[i]=(mp[i]<<1)+sq[i][j];
	}//第一步,把地图转化为二进制
	for(int s=0;s<(1<<m);s++)
	{
		if( ((s&(s<<1))==0) && ((s&(s<<2))==0) && ((s&(s>>1))==0) && ((s&(s>>2))==0) ) 
		{
			legal[++cnt]=s;//储存合法情况 
			judge[cnt]=1;
			int t=s;
			while(t)
			{
				if(t%2==1) sum[cnt]++;
				t=t>>1;
			}
		}
	}//第二步,储存合法状态 
	for(int i=1;i<=cnt;i++) 
	{
		if( (judge[i]) && ( (legal[i]&mp[1])==legal[i]) )
		{
			can[1][i]=1;
			dp[1][i][0]=sum[i];
		}
	}//第三步,找出第一行状态的方案数 
	for(int i=1;i<=cnt;i++) 
	{
		if( (judge[i]) && ( (legal[i]&mp[2])==legal[i]) )
		{
			can[2][i]=1;
			for(int j=1;j<=cnt;j++)
			{
				if(!can[1][j]) continue;
				if((legal[i]&legal[j])==0)
				{
					dp[2][i][j]=max(dp[2][i][j],dp[1][j][0]+sum[i]);
				}
			}
		}
	}//第四步,找出第二行状态的方案数 
	for(int i=3;i<=n;i++)
	{
		for(int now=1;now<=cnt;now++)//当前状态
		{
			if( (judge[now]) && (legal[now]&mp[i])==legal[now] )
			{
				can[i][now]=1;
				for(int s=1;s<=cnt;s++)//上两行状态 
				{
					if(!can[i-2][s]) continue;
					if((legal[s]&legal[now])!=0) continue;
					for(int t=1;t<=cnt;t++)//上一行状态 
					{
						if(!can[i-1][t]) continue;
						if( ((legal[now]&legal[t])!=0) || ((legal[t]&legal[s])!=0) ) continue;
						dp[i][now][t]=max(dp[i][now][t],dp[i-1][t][s]+sum[now]);
					}
				}
			}
		}
	}//第五步,3到n行状压
	for(int i=1;i<=cnt;i++)
	for(int j=1;j<=cnt;j++)
	{
		ans=max(ans,dp[n][i][j]);
	}//记录答案
	printf("%lld\n",ans);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值