【POJ 1456】Supermarket【兩種做法】【二叉堆貪心】【並查集】
題意:
給定N個商品,每個商品有利潤 pi 和過期時間 di,每天只能賣一個商品,過期商品不能再賣,求如何安排每天賣的商品,可以使收益最大。
思路:
先對所有的商品按照時間進行一下排序,再依次將商品加入優先佇列,加入優先佇列的時候需要進行一下判斷:
1.當前商品的日期 == 優先佇列中的商品數量 && 當前商品的價值大於小根堆堆頂元素的價值
那麼將堆頂元素彈出,並將當前商品加入優先佇列
2.當前商品的日期 != 優先佇列中的商品數量
將當前商品加入優先佇列
總結:
本題就是一個貪心的做法,對於與時間排序發生衝突的商品,將衝突商品和之前商品的最小值進行比較,如果衝突商品權值更小,則將商品最小值彈出佇列,最後佇列中剩下的元素都是符合條件,並且最優的。
程式碼:
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <queue> #define rep(i,a,b) for(int i = a;i <= b;i++) using namespace std; typedef long long ll; const int N = 1e4+100; int n; priority_queue<pair<int,int> > q; pair<int,int> goods[N]; bool cmp(pair<int,int> a, pair<int,int> b) { return a.second < b.second; } int main() { while(~scanf("%d",&n)) { while(q.size()) q.pop(); rep(i,1,n) { int x,y; scanf("%d%d",&x,&y); goods[i] = make_pair(-x,y); } sort(goods+1,goods+1+n,cmp); rep(i,1,n) { if(goods[i].second == q.size() && goods[i].first < q.top().first) { q.pop(); q.push(goods[i]); } else if(goods[i].second != q.size()) q.push(goods[i]); } ll ans = 0; while(q.size()) { ans += q.top().first; q.pop(); } printf("%lld\n",-ans); } return 0; }
第二種做法:
思路:
可以發現這道題每一個商品只會佔用一個日期,因此對於商品的價值進行排序,只要這個商品可以選,那麼就選這個商品。
那麼本題就變成了一個簡單的貪心演算法,那和並查集有什麼關係呢?
我們繼續看這個貪心演算法,排序簡單,但是如何判斷這個商品可不可以選呢,就是如何判斷這個商品過期之前的天數有沒有空位呢?之前我們根據優先佇列的大小進行了一次判斷,現在我們通過並查集也可以實現這個判斷。
具體方法是,我們先給所有的日期建立一個數組,一開始每個日期都指向自己。
每當讀入一個商品時,我們去判斷這個商品的日期是否指向0,如果指向0,代表這個商品之前的日期都已經被佔滿了,因此不可填。
如果不是0,則代表這個商品可以填,那麼我們就選取了這個商品,選取完這個商品之後,我們需要將這個 day = get( i ),day就是當前日期所指向的日期。
將這個日期指向 fa[ i ] = get(day - 1),表示這個商品被選取了。
程式碼:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
const int N = 10010;
typedef long long ll;
int fa[N],n;
pair<int,int> goods[N];
int get(int x)
{
if(fa[x] == x) return x;
else return fa[x] = get(fa[x]);
}
int main()
{
while(~scanf("%d",&n))
{
int m = -1;
rep(i,1,n)
{
int x,y;
scanf("%d%d",&x,&y);
goods[i] = make_pair(-x,y);
m = max(m,y);
}
sort(goods+1,goods+1+n);
ll ans = 0;
fa[0] = 0;
rep(i,1,m) fa[i] = i;
rep(i,1,n)
{
int day = get(goods[i].second);
if(day == 0) continue;
ans += goods[i].first;
fa[day] = get(day-1);
}
printf("%lld\n",-ans);
}
return 0;
}