1. 程式人生 > >11.05T5 另類揹包

11.05T5 另類揹包

【問題描述】

   給定n個物品,每個物品可以不選或選一個,第i個物品的價格為ci,價值為vi,出現時間為ti。有m個詢問,每次詢問在出現時間不超過Ti的所有物品中選若干件,總花費不超過Mi的情況下,被選擇物品的價值和的最大值是多少。

【輸入格式】

    第一行輸入n,m。

接下來n行每行3個整數表示ci,vi,ti.

接下來m行詢問每行2個整數表示ti,mi.

【輸出格式】

輸出m行,每行一個整數表示第i個詢問的答案。

【樣例輸入】

5 2

3 6 1

5 7 4

8 2 9

10 1 7

3 3 4

9 50

4 30

【樣例輸出】

   19

   16

【資料規模】

  %30: n≤10,m≤20;  ci,vi,ti≤20,Mi≤1000

  %60: n≤100,m≤1000;  ci,Mi≤100000,vi≤100,ti<=300

  %100: n≤300,m≤100000; ci,Mi≤10^9,vi≤300,ti<=300

 

 

 

 

【分析】另類揹包+單調優化

先將物品時間升序排列,首先想到01揹包,但c和M太大無法承受。
考慮到vi很小:記f[i,j]為1..i件物品選出j的價值和的最小花費,O(N^3),空間剛好足夠。
對於一個詢問的答案就是:
1、算出在Ti時間內所能購買的物品範圍[1,x]
2、答案就是滿足f[x,j]≤Mi的最大j
暴力處理詢問的話依然會TLE
於是二分:
記g[x,j]為f[x,j…]的最小值,這樣g[x]就是單調增的
答案就是滿足g[x,j]≤Mi的最大j

 

 

本人總結:

一般的揹包我們求最大值所以我們的初值可以放心的設定為0
因為即使不能恰好湊到那個數目
我們可以以非0的容量為起點恰好湊到這個數目
但是如果我們改變狀態求達到某個價值的容量最小值
我們明顯不能湊到的地方始終都會是最大值
顯然我們要求到一個價值的容量最小值
其實也就包含了更大的價值的最小值
有可能這個價值不能被恰好湊到,這就是取min的特點
但相對於能湊到更大的價值用更小的空間,我們可以理解成拋棄掉這麼多空間來更新
所以我們這種情況下一定用原來的方法更新完之後要從後往前取保證單調性
因為我們不能從比自己小的價值的最小容量轉移過來,所以只能從後面轉移過來了
在這道題目裡面我們求某價值的最小容量
我們在查詢的之後最小容量相同的情況下我們會找到最大的價值,所以不會漏解
也就是找到剛好湊到的價值
從後往前取min保證二分可以順利進行,這就是意義所在

code:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define N 1000006
 6 using namespace std;
 7 struct node {
 8     long long c,v,t;
 9 } s[N];
10 bool cmp(const node&a,const node&b) {
11     return a.t<b.t;
12 }
13 
14 long long Time[N],f[301][90002],maxVal;
15 long long n,m;
16 long long Find(long long Judge) {
17     long long l=1,r=n,mid;
18     while(l<=r) {
19         mid=(l+r)>>1;
20         if(Time[mid]<=Judge)l=mid+1;
21         else r=mid-1;
22     }
23     return r;
24 }
25 long long Find(long long x,long long Judge) {
26     long long l=0,r=maxVal,mid;
27     while(l<=r) {
28         mid=(l+r)>>1;
29         if(f[x][mid]<=Judge)l=mid+1;
30         else r=mid-1;
31     }
32     return r;
33 }
34 long long read(){
35     long long x=0,f=1;
36     char c=getchar();
37     while(!isdigit(c)){
38         if(c=='-')f=-1;
39         c=getchar();
40     }
41     while(isdigit(c)){
42         x=(x<<3)+(x<<1)+c-'0';
43         c=getchar();
44     }
45     return x*f;
46 }
47 int main() {
48     cin>>n>>m;
49     for(long long i=1; i<=n; i++) {
50         s[i].c=read(),s[i].v=read(),s[i].t=read();
51         maxVal+=s[i].v;
52     }
53     sort(s+1,s+n+1,cmp);
54     for(long long i=1; i<=n; i++)Time[i]=s[i].t;
55     memset(f,0x3f3f3f3f,sizeof f);
56     f[0][0]=0;
57     for(long long i=1; i<=n; i++) {
58         for(long long j=maxVal; j>=0; j--) {
59             if(j>=s[i].v)f[i][j]=min(f[i-1][j],f[i-1][j-s[i].v]+s[i].c);
60             else f[i][j]=f[i-1][j];
61         }
62     }
63     for(long long i=1; i<=n; i++) {
64         for(long long j=maxVal-1; j>=0; j--) {
65             f[i][j]=min(f[i][j],f[i][j+1]);
66         }
67     }
68     for(long long i=1; i<=m; i++) {
69         long long T,M;
70         T=read(),M=read();
71         cout<<Find(Find(T),M)<<'\n';
72     }
73     return 0;
74 }
75 
76 //bag

over