【问题描述】
给定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