[状压DP]JZOJ 1768 NOI2001 炮兵阵地
分析
这题m比较小,考虑二进制状压:0表示放1表示不放
然后发现如果自上而下DP,影响当前的仅有当前行状态和上两行的状态
那么设f[i][j][k]表示当前在第i行,上一行状态为j,当前行状态为k,我们只需要枚举上一行,上二行,当前行就行,但是时间为
O(n*2m*2m*2m)理论超时,而且空间复杂度也就少了一个2m,但是我们发现有许多状态是不必要的,如果一个状态满足:s&(s>>1)||s&(s>>2),这个状态必然不合法,可以用一个数组仅储存合法的状态。
然后枚举时对不合法的状态进行剪枝,可以在时限内跑过
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> using namespace std; int f[101][101][101],b[101],sf[101],cnt[101]; int n,m,l; int main() { char c[11]; scanf("%d%d",&n,&m); for (int i=0;i<n;i++) { scanf("%s",&c); for (int j=0;j<m;j++) b[i]|=(c[j]=='H')*(1<<m-1-j); } for (int s=0;s<1<<m;s++) { if (s&(s>>1)||s&(s>>2)) continue; sf[l]=s; for (int i=0;i<m;i++) if (s&(1<<i)) cnt[l]++; l++; } for (int s=0;s<l;s++) { if (sf[s]&b[0]) continue; f[0][0][s]=cnt[s]; if (n>1) for (int s1=0;s1<l;s1++) { if (sf[s1]&b[1]) continue; if (sf[s]&sf[s1]) continue; f[1][s][s1]=f[0][0][s]+cnt[s1]; } } for (int i=2;i<n;i++) { for (int s=0;s<l;s++) { if (sf[s]&b[i]) continue; for (int s1=0;s1<l;s1++) { if (sf[s1]&b[i-2]) continue; if (sf[s]&sf[s1]) continue; for (int s2=0;s2<l;s2++) { if (sf[s2]&b[i-1]) continue; if (sf[s]&sf[s2]) continue; if (sf[s1]&sf[s2]) continue; f[i][s2][s]=max(f[i][s2][s],f[i-1][s1][s2]+cnt[s]); } } } } int ans=0; for (int s=0;s<l;s++) for (int s1=0;s1<l;s1++) ans=max(ans,f[n-1][s][s1]); printf("%d",ans); }
在日渐沉没的世界里,我发现了你。