POJ 1456 Supermarket【并查集 + 贪心】

超市里有N个商品. 第i个商品必须在保质期(第di天)之前卖掉, 若卖掉可让超市获得pi的利润. 
每天只能卖一个商品.
现在你要让超市获得最大的利润.

Input

多组数据. 
每组数据第一行为一个整数N (0 <= N <= 10000), 即超市的商品数目
之后N行各有两个整数, 第i行为 pi, di (1 <= pi, di <= 10000)

Output

对于每一组数据, 输出当前条件下超市的最大利润

思路:首先按照价值从大到小排序,我们优先选择价值大的同时尽可能把时间往后推。建立一个天数的并查集,初始每天都是一个集合,然后价值从大到小枚举,检查当前d的根节点 rt 是否大于0(所有被选过的最终都会加到0号结点上),大于的话就加上答案,然后把rt 连到rt - 1 结点上。其实就是维护了一个数组中位置的占用情况,这样对于当前商品,你只会选择最靠后的时间。

#include 
#include 
#include 
using namespace std;
typedef long long ll;
#define ls rt << 1
#define rs rt << 1|1
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1|1
const int maxn = 1e4 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int p[maxn], d[maxn], pre[maxn];

void init()
{
    for(int i = 0; i < maxn; ++i)
        pre[i] = i;
}
int Find(int x)
{
    return x == pre[x] ? x : pre[x] = Find(pre[x]);
}
void join(int x, int y)
{
    pre[x] = y;
}
struct node
{
    int p, d;
    bool operator <(const node &r) const
    {
        return p > r.p;
    }
}a[maxn];

int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d%d", &a[i].p, &a[i].d);
        }
        sort(a + 1, a + n + 1);
        ll ans = 0;
        for(int i = 1; i <= n; ++i)
        {
            int rt = Find(a[i].d);
            if(rt > 0)
            {
                ans += a[i].p;
                join(rt, rt - 1);
            }
        }
        printf("%lld\n", ans);
    }
    return 0;
}

 

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