poj1014多重背包--单调队列优化

多重背包,大家都知道时O(n^3)的复杂度。但是这个复杂度在很多题目中时用不上的。所以就有了各种优化,如:二进制分组,堆优化等等。
但是最好的还是单调队列优化.

它时怎么实现的呢?
还是以pod1014的diving来说吧!
在这道题中,可以利用多重背包来进行求解。

首先,单调队列是两端删除,末尾插入的队列,其队列内部满足了单调性。
我们控制好队列的长度以及它的单调性。
这里我们用w,v,sum来代表重量,价值,数量。
首先我以f[x] mod w的剩余类进行分类。
在以重量为w的物品中,只有f[x-w] ,f[x-2*w],f[x-3*w]…f[x-k*w]对答案有影响。
但是不同的f[x-k*w]所加上的k*v是不同的。
这时,就有一个巧妙的方法来解决这个问题。
设x=k*x+c,
那么放进单调队列时将它减去k*v,
如果y=m*x+c,那么取出单调队列的最大值就要加上m*v。
那么取出f[x]时,f[x]=f[x]+(c-a)*v。
这个值,恰好就是所需要加上的价值。

#include
#include
#include
#include
#include
#include
#define For(aa,bb,cc) for(int aa=bb;aa<=cc;++aa)
#define Set(aa,bb) memset(aa,bb,sizeof(aa))
using namespace std;
const int maxn=1000010;
int f[maxn];
int a[10],all,sum;
int q1[maxn],q2[maxn],l1,r1,l2,r2;

void work(){
    Set(f,0);
    all>>=1;
    int w,v,l,k;
    For(i,1,6){
        w=v=i;
        For(j,0,w-1){
            l1=l2=1,r1=r2=0;
            for(k=j,l=0;k<=all;++l,k+=w){
                if(r1-l1==a[i]){//以a[i]的大小取剩余类
                    if(q1[l1]==q2[l2]) ++l2;
                    ++l1;
                }
                int t=f[k]-l*v;//将全体都减去一个大小,放入单调队列中
                q1[++r1]=t;
                while(l2<=r2 && q2[r2]//重新计算
            }
        }
    }
    if(f[all]==all) puts("Can be divided.");
    else puts("Can't be divided.");
    return ;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    while(1){
        all=0;
        For(i,1,6) scanf("%d",&a[i]),all+=a[i]*i;
        if(!all) break;
        ++sum;
        printf("Collection #%d:\n",sum);
        if(all&1){puts("Can't be divided.");puts("");continue;}
        work();
        puts("");
    }
    return 0;
}

你可能感兴趣的:(算法,dp)