JZOJ4702&codeforces534F 状压DP

来源:互联网 发布:专家系统是人工智能吗 编辑:程序博客网 时间:2024/05/05 00:47

题意就不说了。。
不说个毛啊!!!wori我就是题目没看懂所以搞了半天结果搞错了好吗QAQ。。
这里的块如果空间允许一定是两个,如果剩下的空间不足两个才能为1.
。。。。
怎么暴力怎么来。
设f[i][j][a][b][c][d][e]表示第i列,状态为j,5行的块数分别为a,b,c,d,e。
那么转移显然,把之前的全部加上就好了。
对于一个长度为m的行,我们最多能取的块数是m/2+m%2.
那么假设最终要取n个,那么这行最少取n-(n-m)/2-(n-m)%2.
然后就可以按照上面两个公式来枚举a,b,c,d,e。
由于每一列的限制是固定的,那么我们可以预处理出可行的状态,优化时间复杂度,我是设g[i][j]表示块数为i时,第j种状态。。。
细节比较多。

#include<cstdio>#include<algorithm>#include<cstring>#include<iostream>#define fo(i,a,b) for(int i=a;i<=b;i++)using namespace std;const int mo=1e9+7;int f[21][23][11][11][11][11][11],mi[6],a[6],c[6],b[21],g[11][33];int n,m,ans,tot;inline bool check(int y,int x){    int last=0,ans=0;    while (x)    {        if (last==0&&x%2==1)ans++;        last=x%2;        x/=2;    }    if (ans==y)return 1;    return 0;}inline int calc(int x){    return x/2+x%2;}int main(){    scanf("%d%d",&n,&m);    mi[0]=1;    fo(i,1,n)mi[i]=mi[i-1]<<1;    fo(i,1,n)scanf("%d",&a[i]);    fo(i,1,m)scanf("%d",&b[i]);    fo(i,0,10)    fo(j,0,mi[n]-1)    if (check(i,j))g[i][++g[i][0]]=j;    f[0][0][0][0][0][0][0]=1;    fo(i,0,m-1)    fo(j,0,mi[n]-1)    fo(_1,max(a[1]-calc(m-i),0),min(a[1],calc(i)))    fo(_2,max(a[2]-calc(m-i),0),min(a[2],calc(i)))    fo(_3,max(a[3]-calc(m-i),0),min(a[3],calc(i)))    fo(_4,max(a[4]-calc(m-i),0),min(a[4],calc(i)))    fo(_5,max(a[5]-calc(m-i),0),min(a[5],calc(i)))    if (f[i][j][_1][_2][_3][_4][_5])    fo(k,1,g[b[i+1]][0])    {        int x=g[b[i+1]][k];//status k        bool bz=0;        c[1]=_1;        c[2]=_2;        c[3]=_3;        c[4]=_4;        c[5]=_5;        fo(l,1,n)        if (!(j&mi[l-1])&&(x&mi[l-1]))        {            c[l]++;            if (c[l]>a[l])            {                bz=1;                break;            }        }        if (bz) continue;        f[i+1][x][c[1]][c[2]][c[3]][c[4]][c[5]]+=f[i][j][_1][_2][_3][_4][_5];        f[i+1][x][c[1]][c[2]][c[3]][c[4]][c[5]]%=mo;    }    fo(i,1,g[b[m]][0])    ans=(ans+f[m][g[b[m]][i]][a[1]][a[2]][a[3]][a[4]][a[5]])%mo;    printf("%d\n",ans);}
0 0