码迷,mamicode.com
首页 > 其他好文 > 详细

POJ1038 Bugs Integrated, Inc. 状压dp(二进制做法)

时间:2020-02-13 18:47:46      阅读:72      评论:0      收藏:0      [点我收藏+]

标签:int   for   set   cst   i++   alt   时间   splay   ted   

题目网址: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\n",ans);
64 }
65 int main()
66 {
67     scanf("%d",&D);
68     while(D--){
69         data_in();
70         work();
71     }
72     return 0;
73 }
View Code

 

POJ1038 Bugs Integrated, Inc. 状压dp(二进制做法)

标签:int   for   set   cst   i++   alt   时间   splay   ted   

原文地址:https://www.cnblogs.com/Golden-Elf/p/12304587.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!