P2704 [NOI2001] 炮兵阵地----状压dp+滚动数组

题目链接
https://www.luogu.com.cn/problem/P2704

题目大意易懂我就不解释了,直接上分析

分析:我们看图可以知道,大炮所在位置只对上下左右相邻的两个位置有影响,所以我们考虑直接从上到下枚举,这样就不用考虑下面的情况,只需要考虑上左右相邻两个位置是否冲突,我们设dp数组为dp[pre][now][i],表示当前考虑到了第i行并且状态为now,上一行的状态为pre的最优炮兵数,那么状态转移方程就是
dp[pre][now][i] = max(dp[pre][now][i],dp[ppre][pre][i-1]+sum[now])
,ppre表示上上行,sum[now]表示状态为now时1的个数
,我们知道当前这一行的状态只受上两行状态的影响,所以我们不妨先预处理第一行和第二行所有的合法情况,然后从第三行开始状压dp,去维护dp的所有可行状态的最大值即可,但是需要注意的是,这里n达到了100,所以dp[pre][now][i]这里的i我们不能设置为100,不然会MLE,我们需要采用滚动数组,根据前面的状态转移方程我们可以看到,当前第i行的更新只会涉及到第i-1行,所以我们只需要把dp数组的第三维大小开到2即可(滚动数组),这样状态转移方程就变为了dp[pre][now][i%2] = max(dp[pre][now][i%2],dp[ppre][pre][(i-1)%2]).

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int maze[105]={
    
    0};//每一行的地形状态
int sum[1<<10] = {
    
    0};
ll f[1<<10][1<<10][2]={
    
    0};/*f[L][S][i]表示,考虑到了第i行需要滚动数组优化空间,当前状态为s
上一个状态为L的最大放置炮兵的数量*/
int get_number(int number)
{
    
    
	int ans = 0;
	while(number)
	{
    
    
		ans++;
		number&=(number-1);
	}
	return ans;
} 
int n,m; 
int main()
{
    
    
	cin>>n>>m;
	for(int i=0;i<n;i++)
	 {
    
    
	 	char str[20];
	 	cin>>str;
	 	for(int j=1;j<=m;j++)
	 	{
    
    
	 		if(str[j-1]=='H')
	 		maze[i]+=(1<<(m-j));//处理地形 
		}
	 }
	 
	 int N = 1<<m;
	 for(int i=0;i<N;i++) sum[i] = get_number(i);//初始化sum数组 
	 /*现予处理第一行多所有可行情况*/
	 for(int i=0;i<N;i++)
	 {
    
    
	 	if(i&maze[0]) continue;//炮兵不能放在山上 
	 	if(i&(i<<1)) continue;
	 	if(i&(i<<2)) continue;
	 	f[0][i][0] = sum[i];
	 }
	 /*再根据第一行递推第二行的可行情况*/
	 for(int i=0;i<N;i++)//这一行 
	  for(int j=0;j<N;j++)//上一行 
	  {
    
    
	  	/*不满足要求*/
	  	if(i&j) continue;//与相邻两行冲突 
		if(i&i<<1) continue;//i与自己这一行冲突 
		if(i&i<<2) continue;//i与自己这一行冲突
		if(j&j<<1) continue;//j与自己这一行冲突
		if(j&j<<2) continue;//j与自己这一行冲突
	  	if(i&maze[1]) continue;//山顶不能放炮兵 
	  	if(j&maze[0]) continue;//山顶不能放炮兵
		f[j][i][1] = sum[i]+sum[j];
	  }
	  for(int i=2;i<n;i++)
	  {
    
    
	  	for(int s=0;s<N;s++)//当前行 
	  	{
    
    
	  		if((s&s<<1)||(s&s<<2)||(s&maze[i])) continue;
	  		for(int L=0;L<N;L++)//上一行 
	  		{
    
    
	  			if((s&L)||(L&maze[i-1])||(L&L<<1)||(L&L<<2)) continue;
	  			for(int fl = 0;fl<N;fl++)//上上行 
	  			{
    
    
	  				if((fl&s)||(fl&L)||(fl&maze[i-2])||(fl&fl<<1)||(fl&fl<<2)) continue;
	  				f[L][s][i%2] = max(f[L][s][i%2],f[fl][L][(i-1)%2]+sum[s]);
				}
 			}
		}
	  }
	  ll ans = 0;
	  for(int i=0;i<N;i++)
	   for(int j=0;j<N;j++)
	   {
    
    
	   	ans = max(ans,f[i][j][(n-1)%2]);
	   }
	   cout<<ans<<'\n';
	   return 0;
}

おすすめ

転載: blog.csdn.net/TheWayForDream/article/details/116274372