对测 【离线DP+二分】
本人水平有限,题解不到为处,请多多谅解
本蒟蒻谢谢大家观看
题目:
Market
(market.c/cpp/pas)
Input fifile: market.in
Output fifile: market.out
Time limit: 1 seconds
Memory limit: 128 megabytes
在比特镇一共有 n 家商店,编号依次为 1 到 n。每家商店只会卖一种物品,其中第 i 家商店的物品
单价为 ci,价值为 vi,且该商店开张的时间为 ti。
Byteasar 计划进行 m 次购物,其中第 i 次购物的时间为 Ti,预算为 Mi。每次购物的时候,Byteasar
会在每家商店购买最多一件物品,当然他也可以选择什么都不买。如果购物的时间早于商店开张的时间,
那么显然他无法在这家商店进行购物。
现在 Byteasar 想知道,对于每个计划,他最多能购入总价值多少的物品。请写一个程序,帮助
Byteasar 合理安排购物计划。
注意:每次所花金额不得超过预算,预算也不一定要花完,同时预算不能留给其它计划使用。
Input
第一行包含两个正整数 n, m,表示商店的总数和计划购物的次数。
接下来 n 行,每行三个正整数 ci , vi , ti,分别表示每家商店的单价、价值以及开张时间。
接下来 m 行,每行两个正整数 Ti , Mi,分别表示每个购物计划的时间和预算。
Output
输出 m 行,每行一个整数,对于每个计划输出最大可能的价值和。
Examples
market.in market.out
输入:
5 2
5 5 4
1 3 1
3 4 3
6 2 2
4 3 2
3 8
5 9
输出:
10
12
第一个计划可以在商店 2,3,5 各购买一件物品,总花费为 1 + 3 + 4 = 8,总价值为 3 + 4 + 3 = 10。
第二个计划可以在商店 1,2,3 各购买一件物品,总花费为 5 + 1 + 3 = 9,总价值为 5 + 3 + 4 = 12。
40分算法
直接背包 把总花费当做容量,总价值当做val ,再特判一下时间即可
设 f[] 为容量小于等于Mi 的最大价值和
code:
#include<bits/stdc++.h> #pragma GCC optimize(3) using namespace std; int n,m,sum=0,ans=0; int f[1000001]; inline int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } struct oo{ int c,v,t; }a[100010]; struct node{ int tt,mm,id; }b[100010]; bool cmp(oo a,oo b){ return a.t<b.t; } bool comp(node a,node b){ return a.tt<b.tt; } int main() { n=read(),m=read(); for(int i=1;i<=n;i++){ a[i].c=read(),a[i].v=read(),a[i].t=read(); sum+=a[i].c; } for(int i=1;i<=m;i++){ b[i].tt=read(),b[i].mm=read(); b[i].id=i; } sort(a+1,a+n+1,cmp); //sort(b+1,b+m+1,comp); //f[0][0]=0; f[0]=0; for(int j=1;j<=m;j++){ memset(f,0,sizeof(f)); for(int i=1;i<=n;i++){ if(a[i].t<=b[j].tt){ for(int kk=b[j].mm;kk>=a[i].c;kk--){ f[kk]=max(f[kk],f[kk-a[i].c]+a[i].v); // cout<<"f[i][kk]= "<<f[i][kk]<<" i= "<<i<<" kk= "<<kk<<endl; } } //ans+=f[n][b[j].mm]; } printf("%d\n",f[b[j].mm]); } return 0; } /* 5 2 5 5 4 1 3 1 3 4 3 6 2 2 4 3 2 3 8 5 9 */
100分做法:离线DP+二分
c[i](花费)很大但v[i](获得的价值)很小,
f[j]表示获利恰好为j时,所用的最小化费(初始设为正无穷)
方程f[j]=min(f[j],f[j-v[i]]+c[i])
于是对于每一个询问M,直接从后往前扫描,找到第一个小于等于M的值,其下标就是答案
这里这么设置主要是因为考虑到60%时候的方程,当M变大时,v依然很小,于是考虑把下标和数组的值更换一下位置
code:
#include<bits/stdc++.h> #pragma GCC optimize(3) #define N 503 #define LL long long using namespace std; int n,m,ans[100003]; LL f[N*N]; inline int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } struct data{ int c,v,t,pos,num; }b[100003],a[N],c[N]; int cmp(data a,data b){ return a.t<b.t||a.t==b.t&&a.c>b.c; } int cmp1(data a,data b){ return a.pos<b.pos||a.pos==b.pos&&a.c>b.c; } int calc(int x){ int l=1; int r=n; int ans=0; while (l<=r){ int mid=(l+r)/2; if (a[mid].t<=x){ ans=max(ans,mid); l=mid+1; } else{ r=mid-1; } } return ans; } int main(){ n=read(),m=read(); int sum=0; for (int i=1;i<=n;i++) a[i].c=read(),a[i].v=read(),a[i].t=read(),sum+=a[i].v; sort(a+1,a+n+1,cmp); for (int i=1;i<=m;i++) b[i].t=read(),b[i].c=read(),b[i].num=i; for (int i=1;i<=m;i++) b[i].pos=calc(b[i].t); sort(b+1,b+m+1,cmp1); int t=1; while (!b[t].pos&&t<=m) t++; memset(f,127,sizeof(f)); LL inf=f[0]; f[0]=0; for (int i=1;i<=n;i++){ for (int j=sum;j>=a[i].v;j--) f[j]=min(f[j],f[j-a[i].v]+(LL)a[i].c); int last=sum; while (b[t].pos==i&&t<=m){ for (int j=last;j>=1;j--) if (f[j]<=b[t].c){ ans[b[t].num]=j; last=j; break; } t++; } } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }