题目链接:POJ-1456
题目大意
有n件商品,每件商品的利润为,销售日期的截止时间为
(即只能在
时间前销售该物品)。一天只能销售一件物品。问这n件商品的最大利润为多少
方案一
分析
有两种贪心策略,现介绍第一种:
将商品按利润从大到小排序,优先考虑利润大的商品。那么该商品可以在其截止日期前的任一时间销售,那么肯定越晚销售越好,因为越晚销售对其他商品的影响越小,越有可能卖出更多的截止时间在它之前的商品。
核心代码:
p数组储存商品,sold数组记录某一天是否已经被占用了。
假设一件商品的截至时间为d,因为我们希望它能尽可能晚地卖出去,所以从第d天开始不断往前找空闲的时间点,找到之后将该时间点标记为被占用
for(int i=1;i<=N;i++)
for(int j=p[i].deadline;j>=1;j--){
if(!sold[j]){
sold[j]=1;
ans+=p[i].profit;
break;
}
}
复杂度:O()
但是这道题数据比较弱仍然可以过
优化
朴素的做法在枚举每件商品时都会从其截至日期不断往前找没被占用的时间点
我们可以用一个pre数组记录该时间点前最近的一个空闲时间点
比如已知第4-17天都被占用了,而一件商品的截至时间是第16天
那么pre[16]=3,即指向下一个空闲的时间点,我们就将该商品在第3天卖掉即可,而不用从16再枚举到3了。
如果pre[x]=0,则表明1-x天都被占用了,说明该商品不可能卖出去了
这么做就可以避免暴力找解
具体实现:
首先初始化pre数组为-1,表示都空闲
当第x天被占用,我们将pre[x]的值修改为x-1,表示下一个可能空闲的时间点是第x-1天
之所以是可能空闲的时间点,因为可能第x-1天也被占用了
可以联想树结构方便理解:
每一个结点的父结点都是下个可能空闲时间的结点,而根节点就是空闲的时间点
我们用一个_find函数来从结点遍历到根节点就能找到空闲的时间点了
而其实我们希望pre数组能直接指向根节点
因此在查找函数中路径压缩即可
其实这就是一个并查集了,要不断查询某一个时间点的前一个可能空闲时间点,还需要合并操作(如果某时间点x被占用,则合并到时间点x-1的集合中去,成为x-1结点的子树)
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef struct{
int profit;
int deadline;
}product;
product p[10005];
int pre[100005];
bool cmp(product a,product b){
if(a.profit!=b.profit)
return a.profit>b.profit;
return a.deadline>b.deadline;
}
int _find(int x){
return pre[x]==-1?x:pre[x]=_find(pre[x]);
}
int main()
{
int N;
while(~scanf("%d",&N)){
memset(pre,-1,sizeof(pre));
for(int i=1;i<=N;i++)
scanf("%d %d",&p[i].profit,&p[i].deadline);
sort(p+1,p+N+1,cmp);
int ans=0;
for(int i=1;i<=N;i++){
int time=_find(p[i].deadline);
if(time>0){//如果为0则说明这个商品已经没有位置可以放了
ans+=p[i].profit;
pre[time]=time-1;
}
}
printf("%d\n",ans);
}
return 0;
}
方案二
分析
还有另外一种贪心策略,按商品的截止时间从小到大排,优先考虑截止时间前的商品。当发现当前商品截止时间为d,利润为p且无法售卖时(即前d天已经安排了d种商品售卖),寻找这d种商品利润最小的,如果该利润小于p,则可以用当前商品去替换掉利润最小的商品,同样是O()的做法,但是可以利用小根堆进行优化。
代码
#include <cstdio>
#include <string.h>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
const int maxn=1e4+5;
typedef struct{
int profit;
int deadline;
}product;
bool operator < (product a,product b){
return a.deadline<b.deadline;
}
product goods[maxn];
int heap[maxn]; //小根堆
int cnt=1;
void up(int p){//向上调整
while(p>1){
if(heap[p]<heap[p/2]){
swap(heap[p],heap[p/2]);
p/=2;
}
else break;
}
}
void down(int p){ //向下调整
int s=p*2;
while(s<cnt){
if(s<cnt-1&&heap[s]>heap[s+1]) s++;
if(heap[s]<heap[p]){
swap(heap[s],heap[p]);
p=s;
s=p*2;
}
else break;
}
}
void Insert(int value){
heap[cnt]=value;
up(cnt);
cnt++;
}
void Extract(){
heap[1]=heap[--cnt];
down(1);
}
void Remove(int k){
heap[k]=heap[--cnt];
up(k); down(k);
}
void init(){
memset(heap,0,sizeof(heap));
cnt=1;
}
int main()
{
int N;
while(~scanf("%d",&N)){
init();
for(int i=1;i<=N;i++)
scanf("%d %d",&goods[i].profit,&goods[i].deadline);
sort(goods+1,goods+N+1);
int num=0;
for(int i=1;i<=N;i++){
if(goods[i].deadline>num){
Insert(goods[i].profit);
num++;
}
else{
if(goods[i].profit>heap[1]){
Extract();
Insert(goods[i].profit);
}
}
}
int ans=0;
for(int i=1;i<=num;i++)
ans+=heap[i];
printf("%d\n",ans);
}
return 0;
}