JZOJ4702&codeforces534F 状压DP

20 篇文章 0 订阅
15 篇文章 0 订阅

题意就不说了。。
不说个毛啊!!!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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值