[NOI2001]炮兵阵地
Link
容易想出状态 dp[t][i][j] ,表示当前在算第t行,这一行的状态为i,上一行的状态为j , 则状态转移方程就为 dp[t][i][j]=max{dp[t-1][j][k]}+pre[i] 其中k为上上行的状态,且i,j,k都满足题目要求,pre[i] 表示i在二进制下1的个数.
首先考虑读入与预处理。因为在山地不能放炮兵,所以枚举状态时要判断能不能放,在dp过程中用的是二进制来表示状态,所以读入的时候也把每一行的情况转化成二进制。如果是H,则当前位为1,否则为0,例如PHPPH就表示为01001。又考虑到pre[i]要被多次调用,所以先预处理出来。
预处理每行的状态(此处是从0到n-1)
for(rint i=0;i<n;i++){
scanf("%s",s+1);
for(rint j=1;j<=m;j++){
a[i]<<=1;
a[i]|=(s[j]=='H');
}
}
预处理pre数组
inline int Get(int i){
int sum=0;
while(i){sum+=i&1;i>>=1;}
return sum;
}
现在就来判断状态是否合法了
先考虑每一行自身的合法性:
1.炮兵不能放在山地上 , 所以如果 i&a[当前行数] 不为0就跳过。
2.每个炮兵在横向至少隔两格(否则就会互相打到),即 i&(i<<1)||i&(i<<2) 为false。
再考虑相邻的两行的合法性:
3.不能有炮兵同时在一列上,即 i&j 为0
最后考虑隔了一行的两行的合法性:
4.与3相同,炮兵不能同时在一列上,即 i&k 为0
考虑完后就可以开始状态转移了
#define rint register int
#define check(i,lin) (i&a[lin]||i&(i<<1)||i&(i<<2))
for(rint t=2;t<n;t++)
for(rint i=0;i<p;i++){
if(check(i,t)) continue;
for(rint j=0;j<p;j++){
if(check(j,t-1)||(i&j)) continue;
for(rint k=0;k<p;k++){
if(check(k,t-2)||(i&k)||(k&j)) continue;
dp[t][i][j]=maxx(dp[t][i][j],dp[t-1][j][k]+pre[i]);
}
}
}
然而分析一下空间复杂度$ 100*1024^2 $ ,愉快地炸掉了。再回去看代码,发现状态的第一维只与上一项有关,即t只和t-1有关,所以我们考虑压位,把第一维压掉。于是代码就变成了:
for(rint t=2;t<n;t++)
for(rint i=0;i<p;i++){
if(check(i,t)) continue;
for(rint j=0;j<p;j++){
if(check(j,t-1)||(i&j)) continue;
for(rint k=0;k<p;k++){
if(check(k,t-2)||(i&k)||(k&j)) continue;
dp[t&1][i][j]=maxx(dp[t&1][i][j],dp[(t-1)&1][j][k]+pre[i]);
}
}
}
完整代码:
#include<stdio.h>
#define rint register int
#define check(i,lin) (i&a[lin]||i&(i<<1)||i&(i<<2))
#define N 103
#define M 11
int n,m,a[N],pre[1<<M],dp[2][1<<M][1<<M];
char s[N];
inline int Get(int i){
int sum=0;
while(i){sum+=i&1;i>>=1;}
return sum;
}
inline int maxx(int x,int y){return x>y? x:y;}
int main(){
scanf("%d%d",&n,&m);
int p=1<<m;
for(rint i=0;i<n;i++){
scanf("%s",s+1);
for(rint j=1;j<=m;j++){
a[i]<<=1;
a[i]|=(s[j]=='H');
}
}
for(rint i=0;i<p;i++) pre[i]=Get(i);
if(n==1){
int ans=0;
for(rint i=0;i<p;i++)
if(!check(i,0)) ans=maxx(ans,pre[i]);
printf("%d",ans);
return 0;
}
for(int i=0;i<p;i++){
if(check(i,1)) continue;
for(rint j=0;j<p;j++){
if(check(j,0)||(i&j)) continue;
dp[1][i][j]=pre[i]+pre[j];
}
}
for(rint t=2;t<n;t++)
for(rint i=0;i<p;i++){
if(check(i,t)) continue;
for(rint j=0;j<p;j++){
if(check(j,t-1)||(i&j)) continue;
for(rint k=0;k<p;k++){
if(check(k,t-2)||(i&k)||(k&j)) continue;
dp[t&1][i][j]=maxx(dp[t&1][i][j],dp[(t-1)&1][j][k]+pre[i]);
}
}
}
int ans=0;
for(rint i=0;i<p;i++)
for(rint j=0;j<p;j++)
ans=maxx(ans,dp[(n-1)&1][i][j]);
printf("%d",ans);
}