题目链接
题意
给定n种货物,每件货物有他的利润和保质期,销售一种货物需要一天,求最佳销售方案下的最大获利。
思路
利用并查集优化的贪心。
首先是贪心部分,我们要让获利尽量大,就可以每次都拿当前剩余物品中价值最高的,这件物品可以在保质期的任何一天内卖出,那么显然越晚卖出,就越有可能卖出更多保质期短的物品,就越有可能多获利。所以排序后从大到小枚举时间,可以卖出就更新答案,朴素的贪心复杂度O(n²),这道题是可以过得(数据太弱,按理来说1e4怎么都过不了才对),跑了130ms
//主体部分
for(int i=1;i<=n;i++){
int t=p[i].second;
for(int j=t;j>=1;j--)
if(!sold[j]){
sold[j]=1;
ans+=p[i].first;
break;
}
}
如果这题数据足够给力,n²的复杂度是我们无法接受的,那么这里就要考虑优化了。这道题用并查集优化实在是太神了,又进一步加深了一点理解。我们用并查集节点管理天数,对于father数组,我们不再初始化为自身,而是初始化为-1(这可能有悖定义,但理解起来更自然),所有父节点为-1的代表这一天是空闲可卖货的,那么我们按利润大小贪心,查找货物天数的祖先节点X(-1就当作是自己),如果X大于0,说明可以卖出,更新答案,同时将fa[x]设置为x-1,可以理解为这一天和前一天状态相同,最终所有无法卖货的日期会连接同一个祖先节点——0。相当于把朴素贪心的对天数的for循环优化成了路径压缩的并查集查询,自然就加速了。优化后跑了60ms,快了一倍。
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
using namespace std;
typedef long long ll;
const int maxn=10005;
const int inf=0x3f3f3f3f;
pair<int,int>p[maxn];
int n;
int fa[maxn];
void init(){
for(int i=0;i<maxn;i++)
fa[i]=-1;
}
int find(int x){
if(fa[x]==-1)
return x;
return fa[x]=find(fa[x]);
}
//重载排序,规则是价值大的优先,相同价值的保质期长的优先,但其实对保质期排序没意义可删去
bool cmp(pair<int,int>p1,pair<int,int>p2){
if(p1.first>p2.first)
return 1;
else if(p1.first<p2.first)
return 0;
else
return p1.second>p2.second;
}
int main(){
while(scanf("%d",&n)==1){
init();
for(int i=1;i<=n;i++){
scanf("%d%d",&p[i].first,&p[i].second);
}
sort(p+1,p+1+n,cmp);
int ans=0;
for(int i=1;i<=n;i++){
int t=find(p[i].second);
if(t>0){
fa[t]=t-1;
ans+=p[i].first;
}
}
printf("%d\n",ans);
}
}