POJ 1456 Supermarket (贪心 + 暴力 or 优先权队列 or 并查集)

题目链接:

http://poj.org/problem?id=1456


题意分析:
超市里有n件商品,每件商品有对应的利润p和销售的最后期限d,销售一件商品需要花费一个单位时间,给出商品集,问销售这些商品最大能获得多少利润?


解题思路:
因为在第n天出售商品不会影响前面n-1天的出售,可以想到每次出售商品都应在最接近最后期限的时间出售。以此可以想到贪心+暴力的思路,每次取出利润最大的商品,判断能否在最后期限之前出售,若能,将它在最接近最后期限的时间出售,并做标记。


思路一:贪心+暴力 (141ms)

#include
#include
#include
#include
using namespace std;
const int MAXN = 10100;

struct product
{
    int p;
    int d;
    bool operator < (product pp) const
    {
        return p>pp.p || (p==pp.p&&d>pp.d);
    }
}p[MAXN];
bool used [MAXN];
int n;
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%d%d",&p[i].p,&p[i].d);
        sort(p+1,p+n+1);
        int ans = 0;
        memset(used,false,sizeof(used));
        for(int i=1;i<=n;i++)
        {
            for(int j=p[i].d;j>=1;j--)
            {
                if(!used[j])
                {
                    used[j]=true;
                    ans+=p[i].p;
                    break;
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


思路一的算法每次在为商品找可以出售的时间点时浪费了时间,可以使用优先权队列进行优化。

我们只需找到从最大期限到第一天每天能出售的最大利润的商品。
先对所有商品按期限从大到小排序,每次将同一期限的商品全部放入优先权队列中,然后从最大的期限往前一天一天推,每天从优先权队列中取出当天能出售的最大利润的商品。



思路二:贪心+优先权队列(63ms)


#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 10100;

struct product
{
    int p;
    int d;
    bool operator < (product pp) const
    {
        return d>pp.d || (d==pp.d&&p>pp.p);
    }
}p[MAXN];

struct pq_product
{
    int p;
    pq_product(int pp)
    {
        p=pp;
    }
    bool operator < (pq_product pp) const
    {
        return p pq;
        pq_product temp(0);
        for(int i=1;i<=n;i++)
        {
            int nowd=p[i].d;
            temp=pq_product(p[i].p);
            pq.push(temp);
            while(p[i+1].d==p[i].d)
            {
                temp=pq_product(p[++i].p);
                pq.push(temp);
            }
            for(int j=nowd;j>p[i+1].d;j--)
            {
                if(!pq.empty())
                {
                    temp=pq.top();
                    pq.pop();
                    ans+=temp.p;
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}



由于是在并查集专题做到的这道题目,但实在想不出并查集的解法,搜了网上并查集的题解后,才知道并查集在这里是作为工具,查找利润最大的商品可以出售的最晚时间。
可以这样理解,比如,一件期限为6的商品出售之后,下次再出现期限为6的商品时,实际上该商品与期限5的商品相同,因为只能在前5天出售。这样我们便可以利用并查集,出售一个商品后,将这个期限与这个期限的前一天合并(大期限的父亲结点是小期限),这样每次我们只需要找到一个期限的祖先结点,就能知道这个结点最晚可以出售的日期。



思路三:贪心+并查集(63ms)

#include
#include
#include
#include
using namespace std;
const int MAXN = 10100;

struct product
{
    int p;
    int d;
    bool operator < (product pp) const
    {
        return p>pp.p;
    }
}p[MAXN];

int F[MAXN];
int find(int x)
{
    if(F[x]==x)return x;
    return F[x]=find(F[x]);
}

int n;
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=10000;i++)
            F[i]=i;
        for(int i=1;i<=n;i++)
            scanf("%d%d",&p[i].p,&p[i].d);
        sort(p+1,p+n+1);
        int ans = 0;
        for(int i=1;i<=n;i++)
        {
            int fi=find(p[i].d);
            if(fi)
            {
                ans+=p[i].p;
                F[fi]=fi-1;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(并查集,贪心)