Market

在比特镇一共有 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;
}
原文地址:https://www.cnblogs.com/wutongtong3117/p/7673525.html