题目链接:http://poj.org/problem?id=1456
题意:给定N个商品,每个商品有利润pi和过期时间di,每天只能卖一个商品,过期商品不能再卖,求如何安排每天卖的商品,可以使收益最大
解法一:优先队列+贪心
在最优解中,对于每个时间(天数)t,应该在保证不卖出过期商品的前提之下,尽量卖出利润前t大的商品。我们把商品按照过期时间排序,建立一个初始为空的小根堆,存储商品利润,然后扫描每个商品:
①若当前商品的过期日期等于当前堆中的商品个数,说明此天已经被占用了,就需要进行决策。如果当前商品利润比堆顶利润高,则用此商品替换堆顶商品。说明这个之前有选择的商品没有这个利润更高,而用这个商品来取代它。
②若当前商品过期时间大于堆中商品数,则直接加入就好,因为这个日期并没有被占用
代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int MAXN = 1e4+5;
struct Good{
int profit;
int deadline;
}good[MAXN];
int n;
bool cmp(Good g1, Good g2){
return g1.deadline < g2.deadline;
}
int main()
{
while(cin >> n)
{
priority_queue<int, vector<int>, greater<int> > pq;
for(int i = 1; i <= n; ++i){
cin >> good[i].profit >> good[i].deadline;
}
sort(good+1, good+1+n, cmp);
long long ans = 0;
for(int i = 1; i <= n; ++i){
if(good[i].deadline == pq.size()){ //t天前已经卖了t个商品,这时如果这个商品更优,则可以替换选中的一个
if(good[i].profit > pq.top()){
pq.pop();
pq.push(good[i].profit);
}
}
else if(good[i].deadline > pq.size()){ //t天时还没有卖到t个商品,此天没有被占用,则可以直接选中,不用决策
pq.push(good[i].profit);
}
}
while(!pq.empty())
{
ans += pq.top();
pq.pop();
}
cout << ans << endl;
}
return 0;
}
解法二:并查集+贪心
这种解法优先考虑卖出利润大的商品,且对每个商品,在其过期之前尽量晚卖出。这样对于其他商品就有“决策包容性”。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN = 1e4+5;
struct Good{
int profit;
int deadline;
}good[MAXN];
int pre[MAXN];
int n;
int findFather(int x){
if(pre[x] == x) return x;
return pre[x] = findFather(pre[x]);
}
int cmp(Good g1, Good g2){
return g1.profit > g2.profit;
}
int main()
{
while(cin >> n)
{
for(int i = 0; i < MAXN; ++i)
pre[i] = i;
int lim_day = 0;
for(int i = 1; i <= n; ++i){
cin >> good[i].profit >> good[i].deadline;
lim_day = max(lim_day, good[i].deadline);
}
sort(good+1, good+1+n, cmp);
int ans = 0;
for(int i = 1; i <= n; ++i){
int day = findFather(good[i].deadline);
if(day == 0) continue; //安排在第r天卖出
ans += good[i].profit;
pre[day] = findFather(day - 1);
}
cout << ans << endl;
}
return 0;
}