在比特镇一共有 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
5 2
5 5 4
1 3 1
3 4 3
6 2 2
4 3 2
3 8
5 9
market.out
10
12
第一个计划可以在商店 2,3,5 各购买一件物品,总花费为 1 + 3 + 4 = 8,总价值为 3 + 4 + 3 = 10
第二个计划可以在商店 1,2,3 各购买一件物品,总花费为 5 + 1 + 3 = 9,总价值为 5 + 3 + 4 = 12
60%
dp
朴素的01背包肯定会T
那我们就另开一维,以时间为限制
再加上一个01背包
这里写代码片
/*60%*/
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=301;
int n,m;
int c[N],v[N],t[N];
int T,M,f[N][1000001],mx=0;
inline void doit()
{
int i,j,k,ans=0;
for (i=1;i<=mx;i++)
for (j=1;j<=n;j++)
if (t[j]<=i) //符合时间的限制
for (k=300;k>=c[j];k--) //朴素01背包
f[i][k]=max(f[i][k],f[i][k-c[j]]+v[j]);
}
int main()
{
//freopen("market.in","r",stdin);
//freopen("market.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d%d%d",&c[i],&v[i],&t[i]),mx=max(mx,t[i]);
doit();
for (int i=1;i<=m;i++)
{
scanf("%d%d",&T,&M);
int h=0;
for (int j=T;j>=1;j--)
h=max(h,f[j][M]);
printf("%d
",h);
}
return 0;
}
100%
考虑消除掉影响,将物品按时间从小到大排序,
再将询问按照中止位置排序(就是当前询问所能买到的最后一物品的编号),
这样子顺着扫就能消除时间的影响,
只要当中止位置==当前物品的时候更新答案即可,消除掉了时间的影响
那么M是10^9,这个肯定不能作为数组的一维表示,
我们考虑转化一下,将sumv作为一维,进行背包:
f[i][j]表示考虑了前i个商店,获利恰好为j时,所用的最小花费(初始设为正无穷)
方程f[i][j]=min(f[i-1][j],f[i-1][j-v[i]]+c[i])
于是对于每一个询问M,直接从后往前扫描,找到第一个小于等于M的值,其下标就是答案
这里这么设置主要是因为考虑到60%时候的方程,当M变大时,v依然很小,
于是考虑把下标和数组的值更换一下位置
时间复杂度为O(N^3+NM)
看一下码码的时候,需要注意的地方:
这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=501;
int n,m;
struct node{
int c,v,t;
};
node po[N];
int T,M,mx=0;
ll f[N*N];
struct node2{
int t,m,id,ed;
};
node2 q[1000010]; //询问的数据范围
int ans[1000010];
int cmp(const node &a,const node &b)
{
if (a.t!=b.t) return a.t<b.t;
else return a.c>b.c;
}
int cmp2(const node2 &a,const node2 &b)
{ //询问需要按照预算和终止商店排序
if (a.ed!=b.ed) return a.ed<b.ed;
else return a.m>b.m;
}
int ef(int x) //记录一下每个询问可以最远去哪个商店购买
{
int l=1,r=n;
int ans=0;
while (l<=r) //<= l位置上的也要判断一下
{
int mid=(l+r)>>1;
if (po[mid].t<=x) ans=max(ans,mid),l=mid+1;
else r=mid-1;
}
return ans;
}
inline void doit()
{
int i,j,k=1;
while (!q[k].ed&&k<=m) k++; //一个商店也去不了,这种询问跳过
memset(f,0x33,sizeof(f));
f[0]=0;
for (i=1;i<=n;i++)
{
for (j=mx;j>=po[i].v;j--)
f[j]=min(f[j],f[j-po[i].v]+(ll)po[i].c);
int last=mx;
while (q[k].ed==i&&k<=m) //第k个询问可以达到的商店都加入了
{
for (int s=last;s>=1;s--)
if (f[s]<=q[k].m) //花费恰好小于等于
{
ans[q[k].id]=s; //下标就是答案
last=s; //在按照t排序的同时按照m排序,
//所以下一次扫的时候,就不用从头扫了
break;
}
k++;
}
}
}
int main()
{
//freopen("market.in","r",stdin);
//freopen("market.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d%d%d",&po[i].c,&po[i].v,&po[i].t),mx+=po[i].v;
sort(po+1,po+1+n,cmp);
for (int i=1;i<=m;i++)
scanf("%d%d",&q[i].t,&q[i].m),q[i].id=i; //离线操作
for (int i=1;i<=m;i++)
q[i].ed=ef(q[i].t);
sort(q+1,q+1+m,cmp2);
doit();
for (int i=1;i<=m;i++)
printf("%d
",ans[i]);
return 0;
}