P2704 [NOI2001] 炮兵阵地
P2704 [NOI2001] 炮兵阵地
题目翻译:
给定一个 \(n \times m\) 的矩阵,规定那些地方可以放炮塔,那些地方不能放,且每个炮塔上下左右的两格不能有其他炮塔,求最多可以放多少炮塔。
思路:
\(n\) 和 \(m\) 都很小,所以考虑使用状压 \(dp\),由于炮塔的范围涉及两格,所以我们维护时要维护当前格和上一格,在于上上一个一起判断是否合法。但由于还要考虑地形本身的限制,所以我们要预处理出来,用 \(e[i]\) 表示第 \(i\) 行的限制,其储存的东西用二进制储存来说就是能放的为 \(0\) 不能放的为 \(1\) 这样在后面判断的时候直接判断 s1&e[i]==0
即可。
然后在预处理出所有符合限制的状态,用来减少后面的运算。
状态转移:
我们定义 \(dp[i][j][k]\) 表示第 \(i\) 行,上一行状态为 \(j\),当前行状态为 \(k\) 的最多炮台数。对于前两行单独处理,给初始化了,然后从第 \(3\) 行开始。枚举行数,当前行的状态,上一行的状态,上上一行的状态。对于符合限制的状态经行转移:
\[dp[i][k][l]=max(dp[i][k][l],dp[i-1][j][k]+cnt[s1])
\]
其中 \(cnt[s1]\) 表示当前状态的炮台数,也可以预处理出来。这样就可以得出答案。
答案和注意:
最后答案只需要对最后一行的所有 \(dp\) 值取最大即可,枚举两个状态。
注意: 对于 \(n=1,n=2\) 的要进行特判,这里偷懒直接输出答案。
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1050;
int cnt[N],ok[N];
int dp[110][N][N];
int e[N];
int main(){
int n,m;
scanf("%d%d",&n,&m);
int num=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
char c;
cin>>c;
e[i]+=((c=='H')<<(m-j));
}
}
for(int i=0;i<(1<<m);i++){
int tot=0,s=i;
while(s){
if(s&1)tot++;
s>>=1;
}
cnt[i]=tot;
if(!(((i<<1)|(i<<2)|(i>>1)|(i>>2))&i)){
ok[++num]=i;
}
}
for(int i=1;i<=num;i++){
if(!(ok[i]&e[1])){
dp[1][0][i]=cnt[ok[i]];
}
}
for(int i=1;i<=num;i++){
for(int j=1;j<=num;j++){
if(!(ok[i]&ok[j]) && !(ok[i]&e[2]) && !(ok[j]&e[1])){
dp[2][j][i]=cnt[ok[i]]+cnt[ok[j]];
}
}
}
for(int i=3;i<=n;i++){
for(int l=1;l<=num;l++){
int s1=ok[l];
if(!(s1&e[i])){
for(int k=1;k<=num;k++){
int s2=ok[k];
if(!(s2&s1) && !(s2&e[i-1])){
for(int j=1;j<=num;j++){
int s3=ok[j];
if(!(s1&s3) && !(s2&s3) && !(s3&e[i-2])){
dp[i][k][l]=max(dp[i][k][l],dp[i-1][j][k]+cnt[s1]);
}
}
}
}
}
}
}
int ans=0;
for(int i=1;i<=num;i++){
for(int j=1;j<=num;j++){
ans=max(ans,dp[n][i][j]);
}
}
if(n==1){
printf("%d",m<=3?1:4);
return 0;
}
printf("%d\n",ans);
}
状压 \(dp\) 讲解
本文作者:XichenOC
本文链接:https://www.cnblogs.com/XichenOC/p/18689779
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步