[codevs3776]【HR】万花丛中番外之HR家的海豹(误) (状压dp)

数据范围 1<=n<=150,1<=m<=10

m很小,可以考虑状压。

为方便把01互换,找全1矩阵。

2*3的海豹,除了横着贴也可竖着贴,这一行选择只受前两行影响,f[i][j][k]表示海豹数,j是i-1行状态,即j二进制这一位是1表示该点可用且之前没有用,k是i-2行状态。枚举j,k,j是a[i-1]子集,枚举s子集黑科技

for(int j=s;j;j=(j-1)&s)

上2行状态确定后,暴力枚举选海豹,更新答案,这样做比较暴力,需卡常,f要滚动,不用memset,因为同一f[0/1]至少差两行,不会相互影响,且i变大,f不会变小,f[k][a][b] > num,没有必要更新其子集。

#prag\
ma GCC optimize("Ofast")
#include <cstdio>
#include <cstring> 
 
inline void read(int &x) {
    char c; while((c=getchar())<'0'||c>'9');
    x=c-'0';while((c=getchar())>='0'&&c<='9') x=x*10+c-'0';
}

#define R register 

int f[2][1025][1025],ans;

__attribute__((optimize("-Os"))) inline void dfs(R int a,R int b,R int c,R int num,R int k,R int li) {
    if (f[k][a][b] > num) return;
    if (ans < num) ans = num;  
    f[k][a][b] = num;
    R int t = a&b&c;    
    t = t&(t>>1)&li;
    while (t) {
        R int tp = t&-t;
        t ^= tp; 
        R int p = tp|(tp<<1);
        dfs(a^p,b^p,c^p,num+1,k,~(tp-1));
    }
    t = a&b&li;
    t = t&(t>>1)&(t>>2);
    while (t) {
        R int tp = t&-t;
        t ^= tp;
        R int p = tp|(tp<<1)|(tp<<2);
        dfs(a^p,b^p,c,num+1,k,~(tp-1));
    }
}

__attribute__((optimize("-Os"))) inline int work() {
    R int a[152],n,m; memset(a,0,sizeof a); 
    read(n); read(m); 
    for (R int i = 1; i <= n; i++) 
     for (R int j = 0,x; j < m; j++) 
       read(x),a[i] |= (x^1)<<j;
    for (R int i = 2; i <= n; i++) {
      for (R int j = a[i-1]; ; j = (j-1)&a[i-1]) {
       for (R int k = a[i-2]; ; k = (k-1)&a[i-2]) {
           dfs(a[i],j,k,f[(i&1)^1][j][k],i&1,(1<<11)-1);
           if (!k) break;
       }
       if(!j) break;
      }
    }
    printf("%d",ans);
    return 0;
}

int haha = work();
main() {;}

题目描述 Description
HR突然想给家里的墙上贴海豹(误)。
HR家有一面很大很大的墙(HR平时就对着这面墙想ZX)。但是当他生气的时候就会对着这面墙撞啊撞,导致墙上出现了很多窟窿,窟窿上不能贴海豹。这面墙是nm大小的,他想贴很多23的海豹,当然除了横着贴也可竖着贴。
他想让你告诉他,他可以在这面墙上最多可以贴多少海豹。
输入描述 Input Description
输入文件第一行是2个整数 n、m,表示墙大小为n*m。
接下来的n行,每行有m个整数0或1,1表示该位置已经被HR撞出窟窿了,0表示空白。
输出描述 Output Description
最多能贴多少海豹。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值