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

 

你可能感兴趣的:(dp)