P2704 [NOI2001] 炮兵阵地

19 篇文章 0 订阅
14 篇文章 0 订阅

      ~~~~~      P2704 [NOI2001] 炮兵阵地       ~~~~~      总题单链接

思路

      ~~~~~      发现 m ≤ 10 m\leq 10 m10,考虑状压DP。

      ~~~~~      发先同一行有限制,先预处理出满足同一行的限制的数字,最多只有 60 60 60 个。

      ~~~~~       d p [ i ] [ k 2 ] [ k 3 ] dp[i][k2][k3] dp[i][k2][k3] 表示当前考虑了前 i i i 行,第 i − 1 i-1 i1 行为 k 2 k2 k2,第 i i i 行为 k 3 k3 k3 的最大值。

      ~~~~~      转移的时候分别枚举第 i − 2 , i − 1 , i i-2,i-1,i i2,i1,i 行的状态 k 1 , k 2 , k 3 k1,k2,k3 k1,k2,k3。如果合法则 d p [ i ] [ k 2 ] [ k 3 ] = m a x ( d p [ i ] [ k 2 ] [ k 3 ] , d p [ i − 1 ] [ k 1 ] [ k 2 ] + ( k 3 中 1 的个数 ) ) dp[i][k2][k3]=max(dp[i][k2][k3],dp[i-1][k1][k2]+(k3中1的个数)) dp[i][k2][k3]=max(dp[i][k2][k3],dp[i1][k1][k2]+(k31的个数))

      ~~~~~      空间会炸,用滚动数组。还有要记得特判一下 n = 1 n=1 n=1 的情况。

      ~~~~~      时间复杂度 O ( N × 6 0 3 ) O(N\times 60^3) O(N×603),空间复杂度 O ( ( 2 10 ) 2 ) O((2^{10})^2) O((210)2)

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

vector<ll>vec;string s[105];
ll n,m,g[105],dp[2][1030][1030];

signed main(){
	ios::sync_with_stdio(false);
	
	cin>>n>>m;
	for(ll i=1;i<=n;i++){
		cin>>s[i];s[i]='.'+s[i];
		for(ll j=1;j<=m;j++)
			g[i]=g[i]*2+(s[i][j]=='P'?1:0);
	}
	
	for(ll i=0;i<(1ll<<m);i++){
		ll op=1;
		for(ll j=0;j<m;j++){
			if((i&(1ll<<j))&&((i&(1ll<<(j+1)))||(i&(1ll<<(j+2)))))
				{op=0;break;} 
		}
		if(op)vec.push_back(i);
	}
	
	if(n==1){
		ll ans=0;
		for(ll k1:vec){
			if((k1&g[1])!=k1)continue;
			ll asd=0;
			for(ll j=0;j<m;j++)
				asd+=(bool)(k1&(1ll<<j));
			ans=max(ans,asd);
		}
		cout<<ans;
		return 0;
	}
	
	for(ll k1:vec){
		if((k1&g[1])!=k1)continue;
		for(ll k2:vec){
			if((k2&g[2])!=k2)continue;
			if(k1&k2)continue;
			ll asd=0;
			for(ll j=0;j<m;j++)
				asd+=(bool)(k1&(1ll<<j))+(bool)(k2&(1ll<<j));
			dp[0][k1][k2]=asd;
		}
	}
	
	for(ll i=3;i<=n;i++){
		for(ll k1:vec){
			if((k1&g[i-2])!=k1)continue;
			for(ll k2:vec){
				if((k2&g[i-1])!=k2)continue;
				if(k1&k2)continue;
				for(ll k3:vec){
					if((k3&g[i])!=k3)continue;
					if((k1&k3)||(k2&k3))continue;
					ll asd=0;
					for(ll j=0;j<m;j++)
						asd+=(bool)(k3&(1ll<<j));
					dp[1][k2][k3]=max(dp[1][k2][k3],dp[0][k1][k2]+asd);
				}
			}
		}
		for(ll k1:vec)
			for(ll k2:vec)
				dp[0][k1][k2]=dp[1][k1][k2];
	}
	
	ll ans=0;
	for(ll k1:vec)
		for(ll k2:vec)
			ans=max(ans,dp[1][k1][k2]);
	cout<<ans;
	
	return 0;
}
  • 21
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值