POJ1038 Bugs Integrated, Inc. 状压dp(二进制做法)
Posted golden-elf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ1038 Bugs Integrated, Inc. 状压dp(二进制做法)相关的知识,希望对你有一定的参考价值。
题目网址:http://poj.org/problem?id=1038
题意:给出一张N*M的格子纸,其中有一些坏格子,问最多可以在格子纸上切下多少个2*3(3*2)的小矩阵(不包含坏格子)。其中N<=150, M<=10。
做法:
容易发现对于右端在第i列的小矩阵,其能否摆放只和i-1, i-2列有关。因此在方程中记录两列的占用状态。
设f(i,s1,s2)表示第i*M的格子纸,第i-1列的占用状况为s1, 第i列的占用状况为s2的时候,最多可以放多少个小矩阵。
但是很容易发现这个方程不加任何优化极其容易超时,因为光是状态就有约10^8个,但是很容易发现其中有许多状态是根本不可能出现的。可能出现的状态又可以从空状态构造出来,因此多设一个数组acc(i,s1,s2),每一维意义和f相同,表示该状态是否可以通过空状态构造出来。
状态转移,f(i,s1,s2) -> f(i+1,_s2,s3),用刷表法实现,通过现在的状态s1,s2去构造可能的状态s3,也就是决定第i+1列的每个位置是否作为小矩阵的左上角,如果是,那么是2*3还是3*2。
只用被acc标记过的状态去刷表,被刷过的状态的acc标记为1,类推。最后答案在f(N,S1,S2)中搜索一下就可以了。
注意用滚动数组。
时间复杂度:被优化过之后,计算不能,但是可以过题,还算是游刃有余的时间。(一群用三进制的人把我看怕了)
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 using namespace std; 6 const int maxn=155; 7 const int maxm=15; 8 const int maxs=1024; 9 10 int D,N,M,K; 11 bool mark[maxm][maxn]; 12 int cell,f[2][maxs][maxs]; 13 bool acc[2][maxs][maxs],Bad[maxn][maxm][2]; 14 15 bool judge(int i,int m,bool tp){ 16 if(tp) return mark[m][i]||mark[m+1][i]||mark[m+2][i]||mark[m][i-1]||mark[m+1][i-1]||mark[m+2][i-1]; 17 return mark[m][i]||mark[m+1][i]||mark[m][i-1]||mark[m+1][i-1]||mark[m][i-2]||mark[m+1][i-2]; 18 } 19 void data_in() 20 { 21 memset(mark,0,sizeof(mark)); 22 memset(Bad,0,sizeof(Bad)); 23 scanf("%d%d%d",&N,&M,&K); 24 cell=1<<M; 25 int x,y; 26 for(int i=1;i<=K;i++){ 27 scanf("%d%d",&x,&y); mark[y][x]=1; 28 } 29 for(int i=1;i<=M;i++) 30 for(int j=2;j<=N;j++) 31 Bad[j][i][0]=j>2?judge(j,i,0):1,Bad[j][i][1]=judge(j,i,1); 32 } 33 void update(int i,int m,int s1,int s2,int s3,int add) 34 { 35 if(m>=M){ 36 if(f[i-1&1][s1][s2]+add>f[i&1][s2|s3][s3]) f[i&1][s2|s3][s3]=f[i-1&1][s1][s2]+add; 37 acc[i&1][s2|s3][s3]=1; 38 return; 39 } 40 update(i,m+1,s1,s2,s3,add); 41 int x=s1|s2; 42 if(m<M-1&&!(x&1<<m)&&!(x&1<<m+1)&&!Bad[i][m+1][0]) 43 update(i,m+2,s1,s2,s3|1<<m|1<<m+1,add+1); 44 if(m<M-2&&!(s2&1<<m)&&!(s2&1<<m+1)&&!(s2&1<<m+2)&&!Bad[i][m+1][1]) 45 update(i,m+3,s1,s2,s3|1<<m|1<<m+1|1<<m+2,add+1); 46 } 47 void work() 48 { 49 memset(f,0,sizeof(f)); 50 update(2,0,cell-1,0,0,0); 51 for(int i=2;i<N;i++){ 52 memset(acc[i+1&1],0,sizeof(acc[i+1&1])); 53 memset(f[i+1&1],0,sizeof(f[i+1&1])); 54 for(int s1=0;s1<cell;s1++) 55 for(int s2=0;s2<cell;s2++) 56 if(acc[i&1][s1][s2]) update(i+1,0,s1,s2,0,0); 57 int x=0; 58 } 59 int ans=0; 60 for(int s1=0;s1<cell;s1++) 61 for(int s2=0;s2<cell;s2++) 62 if(acc[N&1][s1][s2]) ans=max(ans,f[N&1][s1][s2]); 63 printf("%d ",ans); 64 } 65 int main() 66 { 67 scanf("%d",&D); 68 while(D--){ 69 data_in(); 70 work(); 71 } 72 return 0; 73 }
以上是关于POJ1038 Bugs Integrated, Inc. 状压dp(二进制做法)的主要内容,如果未能解决你的问题,请参考以下文章
POJ 1038 Bugs Integrated, Inc.
POJ1038 Bugs Integrated, Inc 状压DP+优化