这个题目是个挺难表示的状态DP,因为不但要考虑上下还要考虑左右,在DP里面就没有什么下了咯,但也至少除了考虑左右还要考虑上
所以先枚举出在同一行满足条件的状态 即 某状态 若 s&(s<<1) 或者(s&(s<<2)) 则说明同一行的炮塔在射击范围内,要去掉。
然后就是考虑不同行的了,只要上一行和上上行的状态不冲突即可
于是就有了如下状态
dp[i][j][k]代表第i行状态为k,上一行的状态为j时的最大值。
于是它的子状态就是 dp[i-1][w][j]代表上一行的状态为j 上上行的状态为w的最大值。
于是 dp[i][j][k]=max(dp[i][j][k],dp[i-1][w][j]+calc[k]) (calc代表某个状态中1的个数 即炮塔的数目)。
需要枚举四层循环 中间通过状态的对比来去掉一些没用的状态。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define N 11 using namespace std; int dp[105][1<<N-3][1<<N-3]; int A[105],m,n,calc[1<<N],num,state[1<<N]; void init() { num=0; for (int i=0;i<(1<<m);i++) { int tmp=0; if (i&(i<<1) || i&(i<<2)) continue; //左右有冲突 跳过。 state[num]=i; for (int j=0;j<m;j++) { if ((1<<j)&i) tmp++; } calc[num++]=tmp; //算出某个状态中的含1的个数 } } bool fit(int a,int b)//判断上下某个状态是否冲突 { if (a&b) return 0; return 1; } void DP() { int ans=0; for (int i=0;i<num;i++) //先预处理第一行的情况 { if (!fit(state[i],A[1])) continue; dp[1][0][i]=calc[i]; ans=max(ans,dp[1][0][i]); } for (int i=2;i<=n;i++) { for (int k=0;k<num;k++) //这里的是先枚举k还是j 没什么大关系,两个状态彼此独立。 { for (int j=0;j<num;j++) { if (!fit(state[j],state[k])|| !fit(state[k],A[i]) || !fit(state[j],A[i-1]))//有冲突就直接跳过 continue; for (int w=0;w<num;w++) { if (!fit(state[j],state[w])|| !fit(state[k],state[w])) continue; if (!fit(state[w],A[i-2])) continue; dp[i][j][k]=max(dp[i][j][k],dp[i-1][w][j]+calc[k]); ans=max(ans,dp[i][j][k]); } } } } printf("%d ",ans); } int main() { char c; while (scanf("%d%d",&n,&m)!=EOF) { init(); memset(dp,0,sizeof dp); for (int i=1;i<=n;i++) { getchar(); A[i]=0; for (int j=0;j<m;j++) { c=getchar(); if (c=='H') A[i]+=1<<j; //这里故意把H设置为1,是为了正好用上面的fit函数,如果有状态把炮塔建在H上,就是不可取的状态。 // cout<<c; } // cout<<endl; } DP(); } return 0; }