依旧是状态压缩DP(什么叫依旧是,这不是第一道吗)
题目大意
有N*M个方格,每个方格是平原(“P”)或者山地(“H”),如果是平原则可以布置炮兵
每个炮兵的攻击范围是向上向下两格,向左向右两格,以及自己共5格,在攻击范围内不能再布置炮兵,问最多布置多少炮兵
输入输出格式
输入格式:
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符(‘P’或者‘H’),中间没有空格。按顺序表示地图中每一行的数据。N≤100;M≤10。
输出格式:
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
solution
这道题设计状态比较难(对于我来说),但可以考虑一定是从上向下DP的,因此需要记录的状态只有这一行以及上一行(注意,只用记录两行,如果只用一维记录会无法记录状态,三维会MLE),上上行可以通过另一维来得到而且不会MLE
用DP[L][S][i]表示当前是第i行,这一行状态是S,上一行状态是L,那么DP[L][S][i]=max(DP[L][S][i],DP[Fl][L][i-1]+sum[S]); sum[S]表示S这个状态1的个数(即炮兵的个数)
DP比较重要的是设置边界预处理,那么只需要处理前两行的DP值就可以了
几点需要注意的:
1.DP数组第三维需要滚动,不然MLE,考虑到一行的状态只跟上行和上上行有关,所以%3即可
2.DP的优化:DP在枚举行的同时要枚举三个状态,如果放到最里面判断则O(n*23m),TLE妥妥的,因此每次枚举状态都进行判断,这样可以极大的缩短时间
code
#include<cstdio> #include<iostream> #define re register using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int getsum(int x) { int ans=0; while(x) { if(x&1) ans++; x>>=1; } return ans; } int sum[1<<10],all,anss; int ans,sta[105],DP[1<<10][1<<10][3],n,m; char map[105][12]; int main() { n=read();m=read(); all=(1<<m)-1; for(re int i=0;i<n;++i) for(re int j=0;j<m;++j) { cin>>map[i][j]; if(map[i][j]=='H') sta[i]|=(1<<(m-1-j)); } // for(re int i=0;i<n;++i) // printf("%d ",sta[i]); for(int i=0;i<=all;++i) sum[i]=getsum(i); for(re int S=0;S<=all;++S) if(!(S&sta[0]||(S&(S<<1))||(S&(S<<2)))) DP for(re int L=0;L<=all;++L) for(re int S=0;S<=all;++S) if(!(L&S||L&sta[0]||S&sta[1]||(L&(L<<1))||(L&(L<<2))||(S&(S<<1))||(S&(S<<2)))) DP[L][S][1]=sum[S]+sum[L]; for(re int i=2;i<n;++i) for(re int L=0;L<=all;++L) { if(L&sta[i-1]||(L&(L<<1))||(L&(L<<2))) continue; for(re int S=0;S<=all;++S) { if(S&sta[i]||L&S||(S&(S<<1))||(S&(S<<2))) continue; for(re int FL=0;FL<=all;++FL) { if(FL&L||FL&S||FL&sta[i-2]||(FL&(FL<<1))||(FL&(FL<<2))) continue; DP[L][S][i%3]=max(DP[L][S][i%3],DP[FL][L][(i-1)%3]+sum[S]); } } } for(re int L=0;L<=all;++L) for(re int S=0;S<=all;++S) anss=max(anss,DP[L][S][(n-1)%3]); printf("%d ",anss); return 0; }