T2题目:
Time limit: 1seconds
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
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。
题解:
背包dp
这道题n 很小,m 很大,如果直接做m次背包的话一定会TLE.(60分)
所以我们考虑消除掉影响,讲物品按时间从小到大排序,再将询问按照中止位置排序(就是当前询问所能买到的最后一物品的编号),这样子顺着扫就能消除时间的影响,只要
当中止位置=当前物品的时候更新答案即可,消除掉了时间的影响。
那么M是10^9,这个肯定不能作为数组的一维表示,我们考虑转化一下,将sumv作为一维,进行背包,f[i]表示价值为i的最小单价和,然后统计答案的时候从后往前扫,扫到的
第一个满足f[i]<=b[t].c的i就是当前询问的答案。
但是如果对于每个询问都倒着扫一遍,太耗时了。我们发现n很小这就非常好了,我们能否将中止位置相同的一遍扫出来呢?可以的在排序的时候以中止位置为第一关键字(从
小到大),预算为第二关键字(从大到小),这样的话排在后面的预算小,如果预算大的都可以满足,预算小的也可以满足。于是只要中止位置相同就可以一遍扫下来。
时间复杂度为O(N^3+NM)
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define LL long long
using namespace std;
int ans[100005],n;LL f[305*305];
struct dian
{
int c,v,t,pos;
}shop[305],ren[100005];
int cmp(dian a,dian b){return a.t<b.t;}
int cmp1(dian a,dian b)
{
return (a.pos<b.pos) || (a.pos==b.pos&&a.v>b.v);//前面的时间小预算大
}
int erfen(int x)//找到询问所能买到最后一物品的编号
{
int ans=0;
int l=1,r=n,mid;
while (l<=r)
{
mid=(l+r)>>1;
if (shop[mid].t<=x) ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
int main()
{
freopen("market.in","r",stdin);
freopen("market.out","w",stdout);
int m,i,j,k,sum=0;
scanf("%d%d",&n,&m);
for (i=1;i<=n;i++)
{
scanf("%d%d%d",&shop[i].c,&shop[i].v,&shop[i].t);
sum+=shop[i].v;
}
sort(shop+1,shop+n+1,cmp);
for (i=1;i<=m;i++)
{
scanf("%d%d",&ren[i].t,&ren[i].v);
ren[i].c=i; ren[i].pos=erfen(ren[i].t);
}
sort(ren+1,ren+m+1,cmp1);
memset(f,0x7f,sizeof(f));f[0]=0;
int to=1;
while (!ren[to].pos && to<=m) to++;
for (i=1;i<=n;i++)
{
for (k=sum;k>=shop[i].v;k--)//f[i]表示价值为i的最小单价
f[k]=min(f[k],f[k-shop[i].v]+(LL)shop[i].c);
int last=sum;
while (ren[to].pos==i && to<=m)//因为to有<m的条件,所以这个条件一个ans只能进一次,所以前面的商铺一定要达到最好状态,所以需要二分找到一个合适的位置
{
for (j=last;j>=1;j--)
if (f[j]<=ren[to].v)
{
ans[ren[to].c]=j;
last=j;
break;
}
to++;
}
if (to>m) break;
}
for (i=1;i<=m;i++)
printf("%d\n",ans[i]);
}