bzoj1531 [POI2005]Bank notes(单调队列优化多重背包)

单调队列优化多重背包的板子。
我们朴素的多重背包做法是 O ( m c )
可以用二进制优化成 O ( m l o g c )
更进一步的,我们可以用单调队列优化成 O ( n m )
考虑转移方程
f [ j ] = m a x { f [ j k v [ i ] ] + k w [ i ] | 0 k c [ i ] }

决策点不连续,怎么办呢

我们把j按mod v[i] 的余数分类,每一类重新标号,枚举余数d,那么有

f [ j ] = m a x { f [ k ] + ( j k ) w [ i ] | j c [ i ] k j }
其中 f [ j ] 代表 f [ j v [ i ] + d ]

就可以单调队列优化了

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 210
#define M 20010
#define pa pair<int,int>
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,a[N],c[N],m,f[M];
pa q[M];
inline void Min(int &x,int y){if(y<x) x=y;}
int main(){
//  freopen("a.in","r",stdin);
    n=read();memset(f,inf,sizeof(f));f[0]=0;
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<=n;++i) c[i]=read();m=read();
    for(int i=1;i<=n;++i)
        for(int j=0;j<a[i];++j){//divide by remainder
            int qh=1,qt=0;
            for(int k=0;;++k){
                int x=k*a[i]+j;if(x>m) break;
                while(qh<=qt&&k-q[qh].second>c[i]) ++qh;
                while(qh<=qt&&f[x]-k<=q[qt].first) --qt;
                q[++qt]=make_pair(f[x]-k,k);
                Min(f[x],q[qh].first+k);
            }
        }
    printf("%d\n",f[m]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/icefox_zhx/article/details/80900806