黑书1.5.2例题10 Bugs公司
一看m和n差这么多,才10,很有可能是状压。我们这样看好别扭。。把他转一下,变成n*m的矩形,那要压缩一定是把一行压成一个状态。考虑到放的小矩形是3*2或2*3的,会影响(i,x)能否放矩形的只有(i-1,x),(i-2,x)。所以我们用pre[x]这样表示:
pre[x]=0->(i-2,x),(i-1,x)均空闲
pre[x]=1->(i-2,x)被占据,(i-1,x)空闲
pre[x]=2->(i-1,x)被占据
因此我们可以用三进制把需要的状态压缩成一个十进制数。
cur[x]表示:
cur[x]=0->(i-1,x),(i,x)均空闲
cur[x]=1->(i-1,x)被占据,(i,x)空闲
cur[x]=2->(i,x)被占据
显然cur是由pre转移而来的。我们考虑第i行先什么都不放,即除了坏格均为空闲。则显然cur[x]=max{pre[x]-1,0}.每种情况都讨论一下即可。用dp[i][cur]表示前i行,i-1和i行的状态为cur的最大值
接下来我们考虑以(i,x)为左下角如何放置矩形:
1.放3*2矩形:需要(i-2,x)(i-1,x)(i,x)(i-2,x+1)(i-1,x+1)(i,x+1)位置均空闲即pre[x]==0&&pre[x+1]==0&&cur[x]==0&&cur[x+1]==0,
放了之后cur[x]=cur[x+1]=2,再以(i,x+2)为左下角接着放。
2.放2*3矩形:需要(i-1,x)(i,x)(i-1,x+1)(i,x+1)(i-1,x+2)(i,x+2)位置均空闲即cur[x]==0&&cur[x+1]==0&&cur[x+2]==0,
放了之后cur[x]=cur[x+1]=cur[x+2]=2,再以(i,x+3)为左下角接着放
3.不放,则以(i,x+1)为左下角接着放。
我们可以用dfs实现以上矩形的放置,更新dp数组。注意这并不是记忆化搜索,而是帮助dp决策而进行的dfs。空间存不下,我们要用滚动数组。
时间复杂度
O(nm3m)
#include<cstdio>
#include<cstring>
int const N=59050;
int n,m,k,tst,map[151][11],dp[2][N],p=0,pre[11],cur[11];
//用三进制存储上一列与本列的状态;
int tri[12]={0,1,3,9,27,81,243,729,2187,6561,19683,59049};
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline int max(int x,int y){return x>y?x:y;}
inline int toten(int p[]){
int ans=0;
for(int i=1;i<=m;++i) ans+=p[i]*tri[i];return ans;
}
inline void totri(int t,int p[]){
for(int i=1;i<=m;++i) p[i]=t%3,t/=3;
}
void dfs(int x,int cnt,int state){//x--当前要处理的格子,cnt-已经放了多少的芯片,state-当前的状态
dp[p^1][state]=max(dp[p^1][state],cnt);
if(x>=m) return;
//放3*2
if(pre[x]==0&&pre[x+1]==0&&cur[x]==0&&cur[x+1]==0){
cur[x]=cur[x+1]=2;
dfs(x+2,cnt+1,toten(cur));
cur[x]=cur[x+1]=0;
}
//放2*3
if(x+2<=m&&cur[x]==0&&cur[x+1]==0&&cur[x+2]==0){
cur[x]=cur[x+1]=cur[x+2]=2;
dfs(x+3,cnt+1,toten(cur));
cur[x]=cur[x+1]=cur[x+2]=0;
}
//不放
dfs(x+1,cnt,state);
}
int main(){
// freopen("a.in","r",stdin);
tst=read();
while(tst--){
n=read();m=read();k=read();
memset(map,0,sizeof(map));
memset(dp,-1,sizeof(dp));
while(k--){
int x=read(),y=read();
map[x][y]=1;
}
for(int i=1;i<=m;++i) pre[i]=map[1][i]+1;
int state=toten(pre);
dp[p][state]=0;
for(int i=2;i<=n;++i){
for(int j=0;j<=tri[m+1]-1;++j) dp[p^1][j]=-1;
for(int j=0;j<=tri[m+1]-1;++j) if(dp[p][j]!=-1){
totri(j,pre);
//pre[x]=0->(i-2,x),(i-1,x)均空闲
//pre[x]=1->(i-2,x)被占据,(i-1,x)空闲
//pre[x]=2->(i-1,x)被占据
for(int ii=1;ii<=m;++ii){
if(map[i][ii]) cur[ii]=2;
else cur[ii]=max(pre[ii]-1,0);
}
dfs(1,dp[p][j],toten(cur));//dp[p^1][cur]=dp[p][pre]
}p^=1;
}
int ans=0;
for(int i=0;i<=tri[m+1]-1;++i) ans=max(ans,dp[p][i]);
printf("%d\n",ans);
}
return 0;
}