先看同一行哪一些方案可以放置,以减小时间复杂度(老套路)
查看的方法是看左边第一、二,右边第一、二 不能跟自己这个位置同时有1
然后统计这个方法包含的炮兵数,统计方法是线性dp,通过lowbit(树状数组的东西),转移计数。(每个方案的炮兵数量是这个方案的二进制串去除最低位的1的炮兵数量+1)
最后一行一行向前dp,记住原则,一直统计新的一行的平原,并用二进制串表示,再相邻行不断的判断筛选掉不可行的方案,用dp数组向前推进可行方案,每推进一次更新一次ans的最大值,最终输出ans的最大值即可。。
#include <iostream> using namespace std; char map[105][12]; int method[2020],num[2020]; int op[105]; int cnt=0; int dp[105][500][500]; int lowbit(int x) { return x&(-x); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf(" %c",&map[i][j]); for(int i=0;i<(1<<m);i++)//枚举各个方案,选择能够放置的 { if(i&(i>>1)) continue; if(i&(i>>2)) continue; if(i&(i<<1)) continue; if(i&(i<<2)) continue; method[++cnt]=i; } for(int i=1;i<(1<<m);i++) { num[i]=num[i-lowbit(i)]+1; } int ans=0; for(int i=1;i<=n;i++)//一行一行往前dp { for(int j=1;j<=m;j++) { op[i]|=((map[i][j]=='H')<<(j-1)); } for(int j=1;j<=cnt;j++) { if(method[j]&op[i]) continue;//存在放置到山地的方案 for(int k=1;k<=cnt;k++) { if(method[k]&method[j]) continue;//i-1行与i行冲突 int v=num[method[j]];//存放i行新增的方案数 if(i>=2&&(method[k]&op[i-1])) continue;//i-1行放到了山地上 for(int p=1;p<=cnt;p++) { if(method[p] & op[i - 2]) continue; if((method[p] & method[k]) == 0 && (method[j] & method[p]) == 0) { dp[i][j][k] = max(dp[i][j][k],dp[i - 1][k][p] + v); ans=max(ans,dp[i][j][k]); } } } } } printf("%d\n",ans); }