P7689 [CEOI2002] Bugs Integrated,Inc.
极好的状压DP,提供了一种状压的写法。
令人难受的是这道题由于芯片可能会占据三行的空间,所以要用到三进制,而由于某些不可抗因素三进制是不能直接使用位运算来取出的。然后就考虑用 \(f[i][j]\) 来代表第 i 行状态是 j 方案数。这样显然会超时,但一个显然的优化是每一行的合法状态是不多的,毕竟既要保证不占到障碍,不和前一行向前凸的地方冲突,还要保证芯片的形状合格。满足这些条件是需要搜索的,题解提供了一种写起来很舒服的写法,边搜索边更新答案,每搜索到边界就代表获得了一个合法的状态,用之前的状态来更新当前状态即可,还是比较巧妙的。
#include<bits/stdc++.h>
//#define feyn
const int N=100010;
const int S=60;
const int M=15;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline int max(int s1,int s2){
return s1<s2?s2:s1;
}
int m,n,num,g[2][N],p[M],ans;
bool a[S*10][S];
inline int q(int wh,int pl){
return wh/p[pl-1]%3;
}
inline bool able(int wh,int pl,int last){
if(pl>n)return false;
return a[wh][pl]==false&&q(last,pl)==0;
}
inline void f(int wh,int pl,int now,int last,int put){
//第wh行,第pl个,本行now,上一行last,已经放置put个
bool t=wh&1;int data;
if(pl>n){
g[t][now]=max(g[t][now],g[t^1][last]+put);
return;
}
if(data=q(last,pl)){
if(a[wh][pl])return;
f(wh,pl+1,now+(data-1)*p[pl-1],last,put);
return;
}
f(wh,pl+1,now,last,put);
if(able(wh,pl,last)&&able(wh,pl+1,last)){
f(wh,pl+2,now+2*(p[pl-1]+p[pl]),last,put+1);
if(able(wh,pl+2,last)){
f(wh,pl+3,now+p[pl-1]+p[pl]+p[pl+1],last,put+1);
}
}
return;
}
inline void solve(){
memset(a,false,sizeof(a));
memset(g,0xcf,sizeof(g));
ans=0;
read(m);read(n);read(num);
int s1,s2;
while(num--){
read(s1);read(s2);a[s1][s2]=true;
}
g[0][0]=0;
for(int i=1;i<=m;i++){
memset(g[i&1],0xcf,sizeof(g[i&1]));
for(int j=0;j<p[n];j++){
if(g[i+1&1][j]>=0){
f(i,1,0,j,0);
}
}
}
printf("%d\n",g[m&1][0]);
}
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
p[0]=1;for(int i=1;i<=12;i++)p[i]=p[i-1]*3;
int T;read(T);
while(T--)solve();
return 0;
}
一如既往,万事胜意