1. 程式人生 > >BZOJ3163&Codevs1886: [Heoi2013]Eden的新背包問題[分治優化dp]

BZOJ3163&Codevs1886: [Heoi2013]Eden的新背包問題[分治優化dp]

一行 data gis table 一個 ans 進制 玩偶 printf

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\n",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\n",ans);
    }
    return 0;
}

BZOJ3163&Codevs1886: [Heoi2013]Eden的新背包問題[分治優化dp]