POJ - 1456 Supermarket 利用并查集优化贪心

题目链接

POJ-1456

题意

给定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);
			
		}
	}
	

猜你喜欢

转载自blog.csdn.net/TheSunspot/article/details/108030262