蓝书(算法竞赛进阶指南)刷题记录——POJ1038 Bugs Integrated, Inc(三进制状压DP+dfs)

题目:POJ1038.
题目大意:给定一个 n n n m m m列的矩阵,要求用 2 ∗ 3 2*3 23的方块去覆盖(横竖均可),现在给定一些位置不可覆盖,求最多可以放置多少个方块.
1 ≤ n ≤ 150 , 1 ≤ m ≤ 10 1\leq n\leq 150,1\leq m\leq 10 1n150,1m10,多组数据.

这个数据范围一看就是状压,容易想到设 f [ i ] [ j ] [ S ] f[i][j][S] f[i][j][S]表示前 i i i行第 i i i行的覆盖状态为 j j j i − 1 i-1 i1行的覆盖状态为 S S S的最大可放置方块的数量,这个方程可以通过枚举第 i − 2 i-2 i2行的状态转移,时间复杂度经过一些优化可以做到 O ( 2 3 m n ) O(2^{3m}n) O(23mn),过不了.

二维的状态好像有点浪费,因为如果第 i i i行上某个位置是被填的,那么第 i − 1 i-1 i1行上的这个位置的状态已经没用了,不论它填没填都不能被覆盖到.

所以考虑压缩状态,设 f [ i ] [ S ] f[i][S] f[i][S]表示前 i i i行第 i i i行和第 i − 1 i-1 i1行的状态为 S S S时的最大可填方块数量,其中 S S S中第 k k k位为 0 0 0表示第 i i i i − 1 i-1 i1行这个位置都为空,为 1 1 1表示第 i i i行这个位置空了但第 i − 1 i-1 i1行被填,为 2 2 2表示第 i i i行这个位置被填.

然后我们发现这个东西转移很麻烦而且容易TLE(我一开始写了记忆化搜索就TLE了).

考虑在计算状态 f [ i ] [ S ] f[i][S] f[i][S]时,大力dfs搜索第 i − 1 i-1 i1行的状态,每次dfs的时候根据当前要计算的状态调整 j j j就可以了.虽说复杂度上界仍然为 O ( 3 m n ) O(3^mn) O(3mn),但是经过一波优化之后就可以过了.

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=150,M=10,M3=59049,INF=(1<<30)-1;

int n,m,sk,b[N+9][M+9];
int Pow3[M+9];
int dp[2][M3+9],old,now,ans;

void Dfs_dp(int i,int now,int j,int g1,int g2,int v){
  if (j>=m){
  	if (g2<Pow3[m]) dp[now][g2]=max(dp[now][g2],dp[now^1][g1]+v);
  	return;
  }
  int k1=g1/Pow3[j]%3,k2=g1/Pow3[j+1]%3,k3=g1/Pow3[j+2]%3;
  Dfs_dp(i,now,j+1,g1,g2+(k1==2?1:0)*Pow3[j],v);
  if (i>2&&!k1&&!k2&&!b[i][j]&&!b[i][j+1]&&!b[i-1][j]&&!b[i-1][j+1]&&!b[i-2][j]&&!b[i-2][j+1])
    Dfs_dp(i,now,j+2,g1,g2+(Pow3[j]+Pow3[j+1])*2,v+1);
  if (k1<2&&k2<2&&k3<2&&!b[i][j]&&!b[i][j+1]&&!b[i][j+2]&&!b[i-1][j]&&!b[i-1][j+1]&&!b[i-1][j+2])
    Dfs_dp(i,now,j+3,g1,g2+(Pow3[j]+Pow3[j+1]+Pow3[j+2])*2,v+1);
}

Abigail start(){
  Pow3[0]=1;
  for (int i=1;i<=M;++i) Pow3[i]=Pow3[i-1]*3;
  Pow3[M+1]=Pow3[M+2]=1;
}

Abigail into(){
  scanf("%d%d%d",&n,&m,&sk);
  for (int i=1;i<=n;++i)
    for (int j=0;j<m;++j)
      b[i][j]=0;
  int x,y;
  for (int i=1;i<=sk;++i){
  	scanf("%d%d",&x,&y);
    b[x][y-1]=1;
  }
}

Abigail work(){
  old=0;now=1;
  for (int g=1;g<Pow3[m];++g) dp[old][g]=-INF;
  dp[old][0]=0;
  for (int i=2;i<=n;++i){
  	for (int g=0;g<Pow3[m];++g) dp[now][g]=-INF;
  	for (int g=0;g<Pow3[m];++g)
  	  if (dp[old][g]^-INF) Dfs_dp(i,now,0,g,0,0);
  	old^=1;now^=1;
  }
  ans=0;
  for (int g=0;g<Pow3[m];++g) ans=max(ans,dp[old][g]);
}

Abigail outo(){
  printf("%d\n",ans);
}

int main(){
  int T=1;
  scanf("%d",&T);
  start();
  while (T--){
    into();
    work();
    outo();
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值