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

https://www.luogu.com.cn/problem/P2704

[NOI2001] 炮兵阵地

题目描述

司令部的将军们打算在 \(N\times M\) 的网格地图上部署他们的炮兵部队。

一个 \(N\times M\) 的地图由 \(N\)\(M\) 列组成,地图的每一格可能是山地(用 \(\texttt{H}\) 表示),也可能是平原(用 \(\texttt{P}\) 表示),如下图。

在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。

现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

输入格式

第一行包含两个由空格分割开的正整数,分别表示 \(N\)\(M\)

接下来的 \(N\) 行,每一行含有连续的 \(M\) 个字符,按顺序表示地图中每一行的数据。

输出格式

一行一个整数,表示最多能摆放的炮兵部队的数量。

样例 #1

样例输入 #1

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

样例输出 #1

6

提示

对于 \(100\%\) 的数据,\(1 \leq N\le 100\)\(1 \leq M\le 10\),保证字符仅包含 PH

这个题和上面几个一样,就是状态多而已

首先,这是一道状压DP,因为数据量比较状压。。。
分别有\(N * M\)的地图,有缺陷的地形以及十字形的覆盖范围。
然后我们定义状态:
\(F[i][j][k]\)
\(i\)表示当前枚举到第几行,\(j\)表示当前行的状态,\(k\)表示上一行的状态

#include<bits/stdc++.h>
using namespace std;
int n,m,F[105],f[105][66][66],start[70],cnt=0,gs[200];//特殊记录,不爆空间
bool mp[105][30];

int main(){
	cin>>n>>m;
	char a;	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a; 
			if(a=='H')mp[i][j]=1;//另不能选的为1
		}
	}
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			F[i]=(F[i]<<1)+mp[i][j];
            //把每行“不可选”状态压缩起来方便实用
		}
	}
	
	start[++cnt]=0;
    /*这个尤为重要,如果你要空间时间,就得像下面一样存
    如果直接存的话,就没了(毕竟那个一整排H的数据还是有的)
    不过如果从0开始循环也可以,但是放在这里起警示作用233*/
	
	for(int i=1;i<(1<<m);i++){
		if(i&(i<<1))continue;	//因为左二右二不能选
		if(i&(i<<2))continue;
		if(i&(i>>1))continue;
		if(i&(i>>2))continue;
		start[++cnt]=i;//直接存有用的就行
		int x=i;
		while(x){		//求取每个状态的贡献
			gs[cnt]++;
			x-=(x&(-x));
		}
	}
	
	for(int i=1;i<=cnt;i++){	//处理第一排
		if((start[i]&F[1])==0){	//不能与地形冲突
			f[1][i][0]=gs[i];
		}	
	}
	
	for(int i=1;i<=cnt;i++){	//第二排
		if((start[i]&F[2])==0)
		for(int j=1;j<=cnt;j++){
			if((start[i]&start[j])==0&&(start[j]&F[1])==0){
            		//判断是否冲突
				f[2][i][j]=gs[j]+gs[i];
			} 
		}
	}
	//让for来的更猛烈些吧(枚举状态)
	for(int i=3;i<=n;i++){		
		for(int j=1;j<=cnt;j++){	//当前一排状态		
			if((start[j]&F[i])==0){	
				for(int k1=1;k1<=cnt;k1++){		//上面第一排			
					if((start[j]&start[k1])==0&&(start[k1]&F[i-1])==0){						
						for(int k2=1;k2<=cnt;k2++){	//上面第二排				
							if((start[j]&start[k2])==0&&(start[k1]&start[k2])==0&&(start[k2]&F[i-2])==0){	
                            				//判断所有冲突情况
								f[i][j][k1]=max(f[i][j][k1],f[i-1][k1][k2]+gs[j]);//从之前转移过来就行
							}
						}
					}
				}
			} 	
		}
	}
	
	int ans=0;
    //所有的值都在最后1排存,用这一排的所有情况的最大值当最大值
	for(int i=1;i<=cnt;i++){
		for(int j=1;j<=cnt;j++){
			ans=max(ans,f[n][i][j]);
		}
	}
	cout<<ans;
}
posted @ 2024-09-09 20:37  lipu123  阅读(32)  评论(0)    收藏  举报