题目大意
给定n个商品,第i个商品有一个利润vi,和保质期di,每天只能卖一个商品,要求最大化利润。
思路
以前写这题的时候思路是对商品按照利润排序,然后优先出售利润大的商品。假设商品A的利润大于商品B,如果他们保质期都只有1天,那么肯定出售利润高的商品。如果有商品A,B,C,利润A>B>C,且保质期都是2天,那么肯定卖A和B,往下继续同理,对于前t天,尽量卖出前t大的商品。卖一个商品尽量在迟的时间卖,这样子就尽量不会占用保质期比他小的商品了。如果那天被占了就往前面找一天卖,在一个商品利润比另一个和商品利润大的情况下,即使另一个卖不出去了,也要把商品利润高的卖出去,如果前面找不到时间卖就没办法卖。这样的思路纯暴力需要O(n^2)的时间,可以用并查集优化,一个商品卖出之后,就把他的fa数组指向他卖出时间的前一天,这样子卖一个商品,在他保质期之间找一个尽量后面的时间就不需要暴力从后往前找了,只需要用find函数去找就好了。
这里看一下二叉堆的思路,贪心想法也是按照上面的想法,前t天尽量卖出前t大利润的商品,并且尽量迟的时间卖。这里我们将商品按照时间从小到大排序,然后构造一个小根堆(小根堆以利润为权值)。然后逐个扫描排序后的商品。下面分类讨论:
1.如果堆中的商品的数量小于该商品的保质期,说明该商品可以出售,那么就将该商品加入堆中。
2.如果商品数量大于等于该商品的保质期,因为是小根堆,所以当前出售的价值最小的商品是堆顶的商品,如果当前商品的利润大于堆顶商品的利润,那么就把他替换掉,否则不变。这里可能会有一个误区,商品能替换着卖,前提是被替换的商品保质期小于等于要被卖的商品,这里因为我们之前按照商品的保质期从小到大排序了,所以后面比较的商品的保质期一定不会比当前堆中任何商品的保质期小的,所以是可以替换的。这样子通过动态的维护小根堆的方式,动态的维护了前t天尽量卖出前t大的商品。
代码示例
priority_queue
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
int n;
struct node
{
int v,d;//v为利润,d为保质期
}que[100005];
bool cmp(const node& x,const node& y)
{
return x.d<y.d;
}
priority_queue<int,vector<int>,greater<int> >q;
int main()
{
while(~scanf("%d",&n))
{
while(q.size())
{
q.pop();
}
int ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&que[i].v,&que[i].d);
}
sort(que+1,que+1+n,cmp);//对商品按照保质期从小到达排序
for(int i=1;i<=n;i++)
{
if(q.size()<que[i].d)//如果保质期大于当前卖出的商品数(堆中元素个数)就把他卖了(加入堆中)
q.push(que[i].v),ans+=que[i].v;
else//当前商品保质期等于堆中商品数(不会小于,因为是按照保质期从小到大排序,自行领悟一下)
{
if(q.top()<que[i].v)//看看当前卖出最小的商品利润是否比他小,使得话把最小的替换出去
{
ans-=q.top();
ans+=que[i].v;
q.pop();
q.push(que[i].v);
}
}
}
//最终维护出来的小根堆中的元素是卖出之后使得利润最大
printf("%d\n",ans);
}
return 0;
}
手写小根堆
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
struct node
{
int v,d;//v为利润,d为保质期
}que[100005];
int heap[10005];//小根堆
int tot=0;//当前堆中的元素个数
bool cmp(const node& x,const node & y)
{
return x.d<y.d;
}
void init()
{
tot=0;
}
void insert(int x)//堆的插入操作
{
heap[++tot]=x;
int j=tot;
int i=j>>1;
while(i>=1)
{
if(heap[j]<heap[i])
swap(heap[j],heap[i]);
else
break;
j=i;i=j>>1;
}
}
int top()//堆顶元素值
{
return heap[1];
}
void remove()//删除堆顶元素
{
heap[1]=heap[tot--];
int j=1;
int i=j<<1;
while(i<=tot)
{
if(i+1<=tot&&heap[i+1]<heap[i])
i++;
if(heap[i]<heap[j])
swap(heap[i],heap[j]);
else
break;
j=i;i=j<<1;
}
}
int main()
{
while(~scanf("%d",&n))
{
init();
int ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&que[i].v,&que[i].d);
}
sort(que+1,que+1+n,cmp);//对商品按照保质期从小到达排序
for(int i=1;i<=n;i++)
{
if(que[i].d>tot)//如果保质期大于当前卖出的商品数(堆中元素个数)就把他卖了(加入堆中)
insert(que[i].v),ans+=que[i].v;
else//当前商品保质期等于堆中商品数(不会小于,因为是按照保质期从小到大排序,自行领悟一下)
{
if(top()<que[i].v)//看看当前卖出最小的商品利润是否比他小,使得话把最小的替换出去
{
ans-=top();
ans+=que[i].v;
remove();
insert(que[i].v);
}
}
}
//最终维护出来的小根堆中的元素是卖出之后使得利润最大
printf("%d\n",ans);
}
return 0;
}