• BZOJ3163&Codevs1886: [Heoi2013]Eden的新背包问题[分治优化dp]


    3163: [Heoi2013]Eden的新背包问题

    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 428  Solved: 277
    [Submit][Status][Discuss]

    Description

    “寄没有地址的信,这样的情绪有种距离,你放着谁的歌曲,是怎样的心心静,能不能说给我听。”
    失忆的Eden总想努力地回忆起过去,然而总是只能清晰地记得那种思念的感觉,却不能回忆起她的音容笑貌。 记忆中,她总是喜欢给Eden出谜题:在 valentine’s day 的夜晚,两人在闹市中闲逛时,望着礼品店里精巧玲珑的各式玩偶,她突发奇想,问了 Eden这样的一个问题:有n个玩偶,每个玩偶有对应的价值、价钱,每个玩偶都可以被买有限次,在携带的价钱m固定的情况下,如何选择买哪些玩偶以及每个玩偶买多少个,才能使得选择的玩偶总价钱不超过m,且价值和最大。众所周知的,这是一个很经典的多重背包问题,Eden很快解决了,不过她似乎因为自己的问题被飞快解决感到了一丝不高兴,于是她希望把问题加难:多次 询问,每次询问都将给出新的总价钱,并且会去掉某个玩偶(即这个玩偶不能被选择),再问此时的多重背包的答案(即前一段所叙述的问题)。  
    这下Eden 犯难了,不过Eden不希望自己被难住,你能帮帮他么?  

    Input


    第一行一个数n,表示有n个玩偶,玩偶从0开始编号 
    第二行开始后面的 n行,每行三个数 ai, bi, c i,分别表示买一个第i个玩偶需
    要的价钱,获得的价值以及第i个玩偶的限购次数。 
    接下来的一行为q,表示询问次数。 
    接下来q行,每行两个数di. ei表示每个询问去掉的是哪个玩偶(注意玩偶从0开始编号)以及该询问对应的新的总价钱数。(去掉操作不保留,即不同询问互相独立) 

    Output

     
    输出q行,第i行输出对于第 i个询问的答案。 

    Sample Input

    5
    2 3 4
    1 2 1
    4 1 2
    2 1 1
    3 2 3
    5
    1 10
    2 7
    3 4
    4 8
    0 5


    Sample Output

    13
    11
    6
    12
    4


    HINT

     

    一共五种玩偶,分别的价钱价值和限购次数为 (2,3,4), (1,2,1), (4,1,2), (2,1,1),(3,2,3)。五个询问,以第一个询问为例。第一个询问表示的是去掉编号为1的玩偶,且拥有的钱数为10时可以获得的最大价值,则此时剩余玩偶为(2,3,4),(4,1,2),(2,1,1),(3,2,3),若把编号为0的玩偶买4个(即全买了),然后编号为3的玩偶买一个,则刚好把10元全部花完,且总价值为13。可以证明没有更优的方案了。注意买某种玩偶不一定要买光。


    100. 数据满足1 ≤ n ≤ 1000, 1 ≤ q ≤ 3*105 , 1 ≤  a

     

    i、bi、c i ≤ 100, 0 ≤ d i < n,  0  ≤ei ≤ 1000。 

     

     

     


    Source

    1

    直接做完全背包显然不行。
    考虑分治。
    Solve(l,r)表示,当前维护的dp数组,记录的答案是除去[l,r]外的物品的答案。
    Solve(l,mid)时,用[mid+1,r]内的物品转移dp数组。
    当l=r时,将所有ban=l=r的询问一起(查dp数组)求答案。
    时间复杂度O(nwlogn)
     
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    inline void read(int &x){
        register char ch=getchar();x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    }
    const int N=1002;
    const int M=1e6+5;
    struct things{int wei,val,t;}d[N];
    int n,m,p,q[M],ban[M],lim[M],ans[M];
    int dp[11][N];
    inline bool cmp(const int &x,const int &y){return ban[x]<ban[y];}
    //多重背包(用余数搞单调队列优化)
    //一个很好的解释:http://blog.csdn.net/qiusuo800/article/details/8820905 
    inline void trans(int *f,int wei,int val,int t){
        pair<int,int>q[N];
        for(int b=0;b<wei;b++){
            int qh=1,qt=0,now;
            for(int j=0;(now=b+j*wei)<1001;j++){
                for(;qh<=qt&&q[qt].second<=f[now]-j*val;qt--);
                for(;qh<=qt&&j-q[qh].first>t;qh++);
                q[++qt]=make_pair(j,f[now]-j*val);
                f[now]=q[qh].second+j*val;
            }
        }
    }
    void solve(int l,int r,int dep){
        if(l==r){
            for(;p<=m&&ban[q[p]]==l;p++) ans[q[p]]=dp[dep][lim[q[p]]];
            return ;
        }
        int mid=(l+r)>>1;
        memcpy(dp[dep+1],dp[dep],sizeof dp[dep]);
        for(int i=mid+1;i<=r;i++) trans(dp[dep+1],d[i].wei,d[i].val,d[i].t);
        solve(l,mid,dep+1);
        memcpy(dp[dep+1],dp[dep],sizeof dp[dep]);
        for(int i=l;i<=mid;i++) trans(dp[dep+1],d[i].wei,d[i].val,d[i].t);
        solve(mid+1,r,dep+1);
    }
    int main(){
        read(n);
        for(int i=1;i<=n;i++) read(d[i].wei),read(d[i].val),read(d[i].t);
        read(m);
        for(int i=1;i<=m;i++) read(ban[i]),read(lim[i]),ban[i]++,q[i]=i;
        stable_sort(q+1,q+m+1,cmp);p=1;
    //    memset(dp[0],0,sizeof dp[0]);
        solve(1,n,0);
        for(int i=1;i<=m;i++) printf("%d
    ",ans[i]);
        return 0;
    }

    UPD.2

    /*
    离线预处理:多重背包正扫一遍,反扫一遍 
    ans=max(f[k1][j]+f_rev[k1+2][t1-j]){0<=j<=t1} 
    attention:
        习惯了一维的二进制拆分,二维的多重背包居然不会了~~ 
    */
    #include<cstdio>
    #include<iostream>
    using namespace std;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    const int N=1005;
    int n,m,v[N],w[N],c[N],f[N][N],f_rev[N][N];
    void pre_deal(){
        for(int i=1,tmp;i<=n;i++){
            tmp=c[i];
            for(int j=1;j<=1000;j++) f[i][j]=f[i-1][j];
            for(int j=1;tmp!=0;j<<=1){
                j=min(j,tmp);
                tmp-=j;
                for(int k=1000;k>=j*v[i];k--){
                    f[i][k]=max(f[i][k],f[i][k-j*v[i]]+j*w[i]);
                }
            }
        }
        for(int i=n,tmp;i>=1;i--){
            tmp=c[i];
            for(int j=1;j<=1000;j++) f_rev[i][j]=f_rev[i+1][j];
            for(int j=1;tmp!=0;j<<=1){
                j=min(j,tmp);
                tmp-=j;
                for(int k=1000;k>=j*v[i];k--){
                    f_rev[i][k]=max(f_rev[i][k],f_rev[i][k-j*v[i]]+j*w[i]);
                }
            }
        }
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++) v[i]=read(),w[i]=read(),c[i]=read();
        pre_deal();
        m=read();
        for(int k1,t1,ans;m--;){
            k1=read();t1=read();ans=0;
            for(int j=0;j<=t1;j++) ans=max(ans,f[k1][j]+f_rev[k1+2][t1-j]);
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    Xamarin.Forms项目无法添加服务引用
    Xamarin Android长度单位区别
    21IC菜农研究的HotWC3超级CRC运算器
    Delphi天气预报查询
    超外差接收机工作原理?
    ARM汇编指令的特点和速查表
    序列号的设计,不重复的实现一机一码
    iOS第一个简单APP
    GetEnvironmentVariable 获取常用系统变量(转)
    Delphi版的Base64转换函数(修改版)
  • 原文地址:https://www.cnblogs.com/shenben/p/6813730.html
Copyright © 2020-2023  润新知