题解 | #Supermarket#

Supermarket

https://ac.nowcoder.com/acm/problem/106053

题面(摘自vj: https://vjudge.net/problem/POJ-1456#author=yuming)

超市里有N个商品. 第i个商品必须在保质期(第di天)之前卖掉, 若卖掉可让超市获得pi的利润.
每天只能卖一个商品.
现在你要让超市获得最大的利润.
多组数据.
INPUT
每组数据第一行为N, 即超市的商品数目
之后N行数字. 第i行为 pi, di
N , pi, di <= 10000
OUTPUT
对于每一组数据, 输出当前条件下超市的最大利润

我能想到的解法有两个,都是从不同方面进行贪心

反悔贪心

看到题第一眼就想到,这不就是个标准的反悔贪心题吗,不了解的朋友还是可以去了解一下。
网上一位佬的反悔贪心博客:https://djy-juruo.blog.luogu.org/qian-tan-fan-hui-tan-xin
这里只提一嘴,可以这样做(~懒)

代码很大,你忍一下

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cctype>
#include<sstream>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#define pll pair<long long,long long>
#define pii pair<int,int>
#define pdd pair<double,double>
#define ll long long
#define F first
#define S second
#define max(a,b) (a)>(b)?(a):(b)
#define min(a,b) (a)<(b)?(a):(b)
#define abs(a) ((a)<0))?(-1*(a)):(a)
#define INF 0x3f3f3f3f
#define INPUTi1(a) scanf("%d",&(a))
#define INPUTi2(a,b) scanf("%d %d",&(a),&(b))
#define INPUTi3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define INPUTd1(a) scanf("%lf",&(a))
#define INPUTd2(a,b) scanf("%lf %lf",&(a),&(b))
#define INPUTd3(a,b,c) scanf("%lf %lf %lf",&(a),&(b),&(c))
using namespace std;
const int N=10010;
struct node{
    int p,d;
};
bool cmp(node n1,node n2){
    if(n1.d!=n2.d)
        return n1.d<n2.d;
    return n1.p>n2.p;    
}
node e[N];
int main(){
    int n;
    while(~INPUTi1(n)){
        for(int i=0;i<n;i++){
            INPUTi2(e[i].p,e[i].d);
        }
        sort(e,e+n,cmp);
        priority_queue<int,vector<int>,greater<int> >pq;
        long long res=0;
        int cnt=0;
        for(int i=0;i<n;i++){
            if(cnt<e[i].d){
                cnt++;
                res+=e[i].p;
                pq.push(e[i].p);
            }else{
                if(e[i].p>pq.top()){
                    res+=(e[i].p-pq.top());
                    pq.pop();
                    pq.push(e[i].p);
                }
            }
        }
        cout<<res<<"\n";
    }
}

并查集优化贪心

按照正常的贪心想法,因为每天只能卖一件物品。所以我们对于某一天来说,应该优先卖贵的。
为了使得更多天数可以被利用,减少冲突,每一件商品应该等到快过期时卖掉(时间尽量靠后)。这样可以腾出更多天数给其他商品

因此,将商品价格从大到小排序,然后对于每一个商品枚举天数。找到可以卖出的最靠后的某一天,标记之。
但是这样是暴力的贪心,暴力枚举天数会导致超时

可以采用并查集优化这个贪心,这个并查集的写法和普通的并不相同,姑且称之为类并查集

我们观察从后往前观察天数可以发现,如果某天被占用,则会向前找,直到找到一个没有被利用的为止
暴力解法的暴力在于,会重复枚举到被占用的天数。如果能像并查集一样,直接找到根节点/代表元素/最后的没有被占用的天数就好了
初始时给并查集数组 赋值为-1代表其未被占用。之后如果要选择这一天,且未被占用,令,下一次选这一天的时候就直接找到第天。这样合并并查集,对于所有不能选择的天数,最终都会指向0
并查集的具体实现参见代码

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cctype>
#include<sstream>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#define pll pair<long long,long long>
#define pii pair<int,int>
#define pdd pair<double,double>
#define ll long long
#define F first
#define S second
#define max(a,b) (a)>(b)?(a):(b)
#define min(a,b) (a)<(b)?(a):(b)
#define abs(a) ((a)<0))?(-1*(a)):(a)
#define INF 0x3f3f3f3f
#define INPUTi1(a) scanf("%d",&(a))
#define INPUTi2(a,b) scanf("%d %d",&(a),&(b))
#define INPUTi3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define INPUTd1(a) scanf("%lf",&(a))
#define INPUTd2(a,b) scanf("%lf %lf",&(a),&(b))
#define INPUTd3(a,b,c) scanf("%lf %lf %lf",&(a),&(b),&(c))
using namespace std;
const int N=10010;
struct node{
    int p,d;
};
bool cmp(node n1,node n2){
    return n1.p>n2.p;    
}
node e[N];
int p[N];
int find(int u){
    if(p[u]==-1)
        return u;
    p[u]=find(p[u]);
    return p[u];
}
int main(){
    int n;
    while(~INPUTi1(n)){
        for(int i=0;i<n;i++){
            INPUTi2(e[i].p,e[i].d);
        }
        sort(e,e+n,cmp);
        for(int i=0;i<N;i++)
            p[i]=-1;
        long long res=0;
        for(int i=0;i<n;i++){
            int pi=find(e[i].d);
            if(pi){
                res+=e[i].p;
                p[pi]=pi-1;
            }

        }    
    cout<<res<<"\n";

    }
}
全部评论

相关推荐

威猛的小饼干正在背八股:挂到根本不想整理
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务