题意
给你一张有N*M的平原和山地的图,只能在平原上放置炮兵,炮兵的攻击范围如图所示,求最多能放多少个炮兵?
数据规模:N <= 100;M <= 10。
分析
此题比起入门级的状压,难在了状态更多,影响的也不只一行了,而是两行。
所以我们需要加维+减状态
dp[i][j][k],扫到i行为止,当前行的状态为j,上一行的状态为k的方案最大值。
如果再把山地的算进去,状态就太多了,需要预处理剪掉一些。
而相应的,预处理也要预处理1,2两行,枚举第i行时也必须枚举i-1行,i-2行。
减状态的方法:映射。用cnt表示可行状态编号,id[ ]数组对应了这个状态
还需要预处理出每个状态可以放置多少炮兵,便于统计答案
注意好好算一下数组开多大,一念之差很容易RE or MLE
#include<bits/stdc++.h> using namespace std; #define N 110 #define ll long long ll ans=0,dp[N][N][N],num[N]; int n,m,mx,cnt; int e[N][N],ok[N],okk[N][N],id[N]; int mp[N]; string s; int main() { cin>>n>>m; for(int i=1;i<=n;i++) { cin>>s; for(int j=1;j<=m;j++) if(s[j-1]=='P')e[i][j]=1; } mx=(1<<m)-1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) mp[i]=(mp[i]<<1)+e[i][j]; for(int i=0;i<=mx;i++) { if( (((i<<1)&i)==0) & (((i<<2)&i)==0) ) { id[++cnt]=i; ok[cnt]=1; int pos=i; while(pos) { if(pos&1)num[cnt]++; pos>>=1; } } } for(int i=1;i<=cnt;i++) if(ok[i] & ((id[i]&mp[1])==id[i])) { okk[1][i]=1; dp[1][i][0]=num[i]; } for(int i=1;i<=cnt;i++) { if(ok[i] & ((id[i]&mp[2])==id[i])) { okk[2][i]=1; for(int j=1;j<=cnt;j++) { if(!okk[1][j])continue; if( (id[i]&id[j])==0 ) dp[2][i][j]=max(dp[2][i][j],dp[1][j][0]+num[i]); } } } for(int i=3;i<=n;i++) for(int j=1;j<=cnt;j++) { if(ok[j] & ((id[j]&mp[i])==id[j])) { okk[i][j]=1; for(int k=1;k<=cnt;k++) { if(!okk[i-2][k])continue; if( (id[k]&id[j]) )continue; for(int t=1;t<=cnt;t++) { if(!okk[i-1][t])continue; if(((id[k]&id[t])==0) & ((id[t]&id[j])==0)) dp[i][j][t]=max(dp[i][j][t],dp[i-1][t][k]+num[j]); } } } } for(int i=1;i<=cnt;i++) for(int j=1;j<=cnt;j++) ans=max(ans,dp[n][i][j]); cout<<ans; return 0; }