【loli的胡策】NOIP训练7.18(乱搞+背包dp+并查集分治)

来源:互联网 发布:美国海关数据库 编辑:程序博客网 时间:2024/04/27 23:10

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 longusing 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]);  }



阅读全文
1 0
原创粉丝点击