HDU 1171 Big Event in HDU (单调队列优化多重背包)

题目描述

传送门

题目大意:共n个物品,每个物品有一个大小和数量,将所有的物品分成两部分,是两部分的大小尽量平均

题解

看成是多重背包,背包的体积是sum/2,求能装下物品的最大大小和。

然后就是用单调队列优化的问题了。

正常的转移: f[i][j]=max(f[i][jkv[i]]+kw[i],f[i][j])  0<=k<=c[i] 这样的之间复杂度是 O(Vc[i])
比较常见的优化是二进制优化,将c[i]分成 0,1,2,4,....,2k ,然后再做01背包,时间复杂度是 O(Vlogc[i])
其实还能继续优化,用单调队列可以优化到 O(Vn)
a=j/v[i],b=j%v[i] ,那么j就能表示成 j=av[i]+b ,用k表示取第i件物品的件数比a少k件。
那么转移可以表示成
f[i][j]=max(f[i][j],(f[i1][b+kv[i]]kw[i])+aw[i]
那么枚举i,j后真正的变量就只有k了,用单调队列维护 f[i1][b+kv[i]]kw[i] 在合法范围内的最大值即可。

比较具体的讲解

代码

#include
#include
#include
#include
#include
#define N 1005
#define M 250003
using namespace std;
int n,m,A,B,head,tail,head1,tail1;
int v[N],c[N],q[M],q1[M],f[M];
int main()
{
    freopen("a.in","r",stdin);
    while (true) {
        scanf("%d",&n);
        if (n<0) break;
        m=0;
        for(int i=1;i<=n;i++) {
            scanf("%d%d",&v[i],&c[i]);
            m+=v[i]*c[i];
        }
        for (int i=0;i<=m;i++) f[i]=0;
        int sum=m;
        m=m>>1;
        for (int i=1;i<=n;i++) 
         for (int j=0;j0;
            head1=tail1=0;
            for (int k=j,cnt=0;k<=m;k+=v[i],cnt++) {
                if (tail-head==c[i]+1) {
                    if (q1[head1+1]==q[head+1]) head1++;
                    head++;
                 }
                int t=f[k]-cnt*v[i];
                q[++tail]=t;
                while (head11]+cnt*v[i];
            }
         }
         B=f[m];
         A=sum-B;
         printf("%d %d\n",A,B);
    }
}

你可能感兴趣的:(动态规划)