状压DP:炮兵阵地

一开始我只开了二维数组来存储状态,怎么都想不出

后来百度了一下,才知道正解

按层数来dp,如果用dp[i][j][k]来表示在第i行,状态为j,i-1行状态为k时的状态,那么有转移方程f[i][j][k]=max(f[i][j][k],f[i-1][k][l]+bit[i]);
枚举i(层数),j(当前层状态),k(上一层状态),l(上上层状态)就可以来进行转移了。

然而,由于有最多1024种状态,真正可行的只有大概70种,所以必须先预处理出来存着,这样状态数组f也可以省下空间。

(上面预处理步骤不可偷懒,否则TLE/MLE)

地图的每一层也可以变为一个二进制数,在判断可行性时更方便,但是我太懒了没有写。

上代码:

#include<bits/stdc++.h>
#define RI register int
using namespace std;
int n,m,l,cnt,ans;
int bit[1<<11],f[110][70][70],sta[70];
char mp[110][12];
inline bool ok1(int x,int v)
{
	for (RI i=1;i<=m;i++) if (mp[x][i]!='P'&&((sta[v]>>(i-1))&1)) return 0;
	return 1; 
}
inline bool ok2(int x,int y){return !(sta[x]&sta[y]);}
inline bool ok3(int x)
{
	int a=(x>>1),b=(x>>2);
	if ((a&x)||(b&x)) return 0;
	return 1;
}
int num(int x)
{
	int ret=0;
	while (x) x-=(x&-x),ret++;
	return ret;
}
void in()
{
	scanf("%d %d",&n,&m);
	for (RI i=1;i<=n;i++)
		for (RI j=1;j<=m;j++)
			cin>>mp[i][j];
	return;
}
void pre()
{
	l=(1<<m)-1;
	for (RI i=0;i<=l;i++)
		if (ok3(i))
		{
			sta[++cnt]=i;
			bit[cnt]=num(i);
		}
	for (RI i=1;i<=cnt;i++) if (ok1(1,i))
	{
		 f[1][i][0]=bit[i];
		 ans=max(ans,bit[i]);
	}
	if (n==1) return;
	for (RI i=1;i<=cnt;i++) if (ok1(2,i))
	{
		for (RI j=1;j<=cnt;j++) if (ok1(1,j)&&ok2(i,j))
		{
			f[2][i][j]=max(f[2][i][j],f[1][j][0]+bit[i]);
			ans=max(ans,f[2][i][j]);
		}
	}
	return;
}
void solve()
{
	for (RI i=3;i<=n;i++)
		for (RI j=1;j<=cnt;j++)
			if (ok1(i,j))
				for (RI k=1;k<=cnt;k++)
					if (ok1(i-1,k)&&ok2(j,k))
						for (RI r=1;r<=cnt;r++)
							if (ok1(i-2,r)&&ok2(j,r)&&ok2(k,r)){
								f[i][j][k]=max(f[i][j][k],f[i-1][k][r]+bit[j]);
								ans=max(ans,f[i][j][k]);
							}
	printf("%d",ans);
	return;
}
int main()
{
	in();
	pre();
	solve();
	return 0;
} 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值