超市

超市

有n件商品,第i件商品标记为\((d_i,p_i)\),表示该件商品保质期\(d_i\)天,价值\(p_i\),现在从第一天开始,每天只能卖一件商品,请最大化收益,\(n\leq 10000\)

这显然是一道集合的题目,无序的话,首先考虑排序,转为有序的数列

法一:按\(p_i\)从大到小排序

从数列前往后扫描,容易知道第一件商品价值最大,朴素的思想告诉我们要选择这一件商品,但是一定会最优吗?从答案的角度看问题,把每一天想象为一个盒子,而每件商品为一个球,某些商品只能放在前面特定的盒子。

在最终的方案中,盒子应该被放的不能再放任何球进入,现在回到问题来,如果不选这个球,就代表,腾出这个盒子给了别的球,而别的球价值显然低了,于是结果肯定不优秀。

现在问题显然是球应该放到哪一天?朴素的思想告诉我们应该放在可以放的天数中尽可能靠后的天,因为对于两天\(x,y,x<y\)而言,后面的盒子只有三种可能,\(x,y\)都可以选择,\(x\)能选择,\(y\)不能选择,\(x,y\)都能选择,无论哪种情况,结果一定是最优秀的。

因此我们只需要从前往后扫描,把能选的都选了,选的天数尽可能靠后即可,为了优化\(O(n^2)\)扫描,你可以利用线段树,但是不好写,我们不妨利用平衡树,把每一天的编号加入树中,如果某一天选择卖出一件商品的话,就在树中删除对应的天的编号,如果需要查询是否它是否可以选择,只要把其对应的保质期在平衡树查找第一个小于等于它的即可,时间复杂度\(O(nlog(n))\),常数太大了,作为比较差劲的法一。

参考代码:

#include <iostream>
#include <cstdio>
#include <set>
#include <cstring>
#include <algorithm>
#define il inline
#define ri register
#define Size 15000
using namespace std;
struct item{
    int p,d;
    il bool operator<(const item&x)const{
        return p>x.p;
    }
}I[Size];
set<int>S;
set<int>::iterator m;
il void read(int&);
int main(){int n;
    while(scanf("%d",&n)!=EOF){int ans(0);S.clear();
        for(int i(1);i<=10000;++i)S.insert(i);
        for(int i(1);i<=n;++i)read(I[i].p),read(I[i].d);
        sort(I+1,I+n+1);
        for(int i(1);i<=n;++i){
            m=S.upper_bound(I[i].d);
            if(m==S.begin())continue;
            S.erase(--m),ans+=I[i].p;
        }printf("%d\n",ans);
    }
    return 0;
}
il void read(int &x){
    x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}

法二:按\(d_i\)

从数列前往后扫描,你只知道,这一件物品保质期是最短的,如果说法一是最优性贪心(就是按最优候选人选到最差候选人,能选的都选,比如krucal算法),那么法二就是替换性贪心,通俗地讲,也就是一开始做的不一定是最优决策,但是后面会不断替换最差解,从而使结果最优。

扫描二维码关注公众号,回复: 6872374 查看本文章

不妨维护一个小根堆,排序关键字为\(d_i\),从前往后扫描数列,显然堆中元素个数总是小于当前商品的保质期,如果该件商品的保质期大于堆中元素个数,加入堆中,否则就不得不替换一件商品,显然是替换最差劲的商品,也就是堆顶,因此就可以做到常数比较小的\(O(nlog(n))\)(法一用到了天数这个条件,所以萎了)。

顺便提一下,为什么不按\(d_i\)排序不行?因为替换性贪心有一个前提,你要能够替换,如果不按\(d_i\)排序最终导致,你替换了不能替换的比如你只有1天保质期,而你替换了有几十天保质期,又靠后的商品。

参考代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#define il inline
#define ri register
#define Size 10500
#define swap(x,y) x^=y^=x^=y
using namespace std;
struct big{
    int a[Size],n;
    il void push(int x){
        a[++n]=x;int p(n);
        while(p>1)
            if(a[p>>1]>a[p])
                swap(a[p>>1],a[p]),
                    p>>=1;
            else break;
    }
    il void pop(){
        swap(a[1],a[n]),--n;
        int p(1),s(p<<1);
        while(s<=n){
            if(s<n&&a[s+1]<a[s])++s;
            if(a[s]<a[p])swap(a[s],a[p]),
                             p=s,s=p<<1;
            else break;
        }
    }
}B;
struct pi{int p,d;
    il bool operator<(const pi&x)const{
        return d<x.d;
    }
}p[Size];
il void read(int&);
int main(){int n;
    while(scanf("%d",&n)!=EOF){
        for(int i(1);i<=n;++i)
            read(p[i].p),read(p[i].d);
        sort(p+1,p+n+1);
        for(int i(1);i<=n;++i){
            if(p[i].d>B.n)B.push(p[i].p);
            else if(B.a[1]<p[i].p)
                B.pop(),B.push(p[i].p);
        }int ans(0);
        for(int i(1);i<=B.n;++i)
            ans+=B.a[i];printf("%d\n",ans),B.n=0;
    }
    return 0;
}
il void read(int &x){
    x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}

猜你喜欢

转载自www.cnblogs.com/a1b3c7d9/p/11248155.html