poj1038 Bugs Integrated, Inc.(3进制状压dp)

黑书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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值