• 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;
    }
  • 相关阅读:
    dragloader.js帮助你在页面原生滚动下实现Pull Request操作
    移动Web单页应用开发实践——实现Pull to Request(上/下拉请求操作)
    【转】移动Web单页应用开发实践——页面结构化
    前端网址收集!Amazing! 神奇!
    前端代码书写规范
    扫描工具 分析
    backtrack5渗透 笔记
    Sqlmap注入技巧集锦
    nmap 高级扫描用法
    小温谈数据库优化--数据设计篇(转)
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673525.html
Copyright © 2020-2023  润新知