cf298F:状压dp+剪枝

div2的F题,只想到了一个复杂度略高的dp,T了几次,后来加了剪枝减掉一些无用的状态终于过了。。 

题意:

一个n*m的矩阵 (n<=5,m<=20),对格子进行黑白染色,已经给出了每行每列黑色联通块的个数,要求输出一组答案,满足有解。

思路:

首先发现n只有5,考虑按列处理,每列总共有2^5=32种状态;又发现对于给出的联通块个数,最坏情况是当联通块个数等于1或者2的时候有15种情况

比直接二进制处理快了一倍。所以我们可以预先处理出联通块个数为0~3时对于的二进制状态;

找到了列的基本状态,现在来考虑转移:

首先在转移时应该满足每行的黑色联通块个数,所以每行当前的联通块个数是需要保存的,由于m<=20所以每行最多有0~10这11种情况,考虑到5行就总共有11^5种状态

所以总共的状态数即为 20*15*11^5=48315300 由于cf的机器很强大这个状态数基本算是可以接受了

转移时处理如下:对于每一行如果上一列为0且当前列为1,则联通块数目+1,其他情况联通块数目不变。

这样我们就可以解决整个dp的过程了,由于题目要求输出染色方案,我们就需要记录每一个状态的前驱,最后通过前驱获得答案

但是直接交上去还是会T的,这里有两个剪枝可以使用

1.在转移过程中如果当前行的联通块个数已经大于题目给的个数,直接把这个状态减掉

2.在转移过程中如果当前行的联通块个数在剩下的列里怎么取(最好情况为一黑一白这样染)都不可能达到题目给的个数,把这个状态减掉

最后终于ac了!

代码:

  1 #include <iostream>
  2 #include <stdio.h>
  3 #include<string.h>
  4 #include<algorithm>
  5 #include<string>
  6 #include<ctype.h>
  7 #include<vector>
  8 using namespace std;
  9 #define MAXN 10000
 10 bool dp[20][16][161060];
 11 int pret[49000000];
 12 int x[6];
 13 int y[21];
 14 int p[7];
 15 int ans[21];
 16 int  v[21][21];
 17 int nn[21];
 18 int n,m;
 19 int fun(int s)
 20 {
 21     int res=0;
 22     int pre=0;
 23     for(int i=0; i<n; i++)
 24     {
 25         if((s&(1<<i))&&pre==0)
 26         {
 27             res++;
 28         }
 29         pre=(bool)(s&(1<<i));
 30     }
 31     return  res;
 32 }
 33 inline int get(int s,int i)
 34 {
 35     return (s%p[i+1])/p[i];
 36 }
 37 int fuck(int s,int pre,int now,int pos)
 38 {
 39     int res=0;
 40     for(int i=0; i<n; i++)
 41     {
 42         int tmp=(get(s,i)+((!(pre&(1<<i)))&&(now&(1<<i))));
 43         if(tmp>x[i])
 44             return -1;
 45         if(tmp+(m-pos)/2<x[i])
 46             return -1;
 47         res+=tmp*p[i];
 48     }
 49     return res;
 50 }
 51 inline int make(int i,int j,int s)
 52 {
 53     return s+j*161051+i*15*161051;
 54 }
 55 
 56 inline int getj(int t)
 57 {
 58     return (t%(15*161051))/161051;
 59 }
 60 
 61 char s[30][30];
 62 int main()
 63 {
 64     cin>>n>>m;
 65     p[0]=1;
 66     for(int i=1; i<=6; i++)
 67     {
 68         p[i]=p[i-1]*11;
 69     }
 70     for(int i=0; i<(1<<n); i++)
 71     {
 72         int tmp=fun(i);
 73         v[tmp][nn[tmp]++]=i;
 74     }
 75     for(int i=0; i<n; i++)
 76     {
 77         cin>>x[i];
 78     }
 79     for(int i=0; i<m; i++)
 80     {
 81         cin>>y[i];
 82     }
 83     for(int i=0; i<nn[y[0]]; i++)
 84     {
 85         dp[0][i][fuck(0,0,v[y[0]][i],0)]=1;
 86     }
 87     int now,pre,st;
 88     for(int i=1; i<m; i++)
 89     {
 90         for(int j=0; j<nn[y[i-1]]; j++)
 91         {
 92             for(int s=0; s<161051; s++)
 93             {
 94                 if(dp[i-1][j][s])
 95                 {
 96                     for(int k=0; k<nn[y[i]]; k++)
 97                     {
 98                         st=fuck(s,v[y[i-1]][j],v[y[i]][k],i);
 99                         if(st<0)
100                             continue;
101                         dp[i][k][st]=1;
102                         pret[make(i,k,st)]=make(i-1,j,s);
103                     }
104                 }
105             }
106         }
107     }
108     now=0;
109     for(int i=0;i<n;i++)
110     {
111         now+=x[i]*p[i];
112     }
113     int j=-1;
114     for(int i=0;i<nn[y[m-1]];i++)
115     {
116         if(dp[m-1][i][now])
117         {
118             j=i;
119             break;
120         }
121     }
122 
123     now=make(m-1,j,now);
124     for(int i=m-1;i>=0;i--)
125     {
126         ans[i]=v[y[i]][getj(now)];
127         now=pret[now];
128     }
129     for(int i=0;i<n;i++)
130     {
131         for(int j=0;j<m;j++)
132         {
133             s[i][j]=((ans[j])&(1<<i))?'*':'.';
134         }
135     }
136     for(int i=0;i<n;i++)
137     {
138         puts(s[i]);
139     }
140     return 0;
141 }
View Code

 

posted @ 2015-04-24 11:26  PlasticSpirit  阅读(447)  评论(0编辑  收藏  举报