Supermarket POJ - 1456 (并查集)

超市里有N个商品. 第i个商品必须在保质期(第di天)之前卖掉, 若卖掉可让超市获得pi的利润.
每天只能卖一个商品.
现在你要让超市获得最大的利润.

Input

多组数据.
每组数据第一行为一个整数N (0 <= N <= 10000), 即超市的商品数目
之后N行各有两个整数, 第i行为 pi, di (1 <= pi, di <= 10000)

Output

对于每一组数据, 输出当前条件下超市的最大利润

Sample Input

4
50 2
10 1
20 2
30 1

7
20 1
2 1
10 3
100 2
8 2
5 20
50 10

Sample Output

80
185

这道题目一开始想用贪心来做,也可以使用并查集来做,使用并查集只是在查找速度上面更快一点,使用普通的贪心也是可以AC的。这道题目的意思就是给出一些商品和他们的保质期,在保质期内将商品卖出的受益最大,但是每天只能卖出一件,所以我们得合理的卖出商品,将最贵的先卖出去,然后看看保质期,谁还能卖出去,再去卖谁,就这样,一直卖到最后就是最大值。

普通贪心,将商品价钱从大到小排序,然后用标记数组来标记商品(标记数组下标是第几天,就是判断这个商品在这天卖没有卖出去,因为一天只能卖出一个商品,我们就得先卖出去那个最贵的商品,然后再看其他的商品),如果当前商品没有被标记,我们就直接卖掉就可以了,如果被标记了,我们再从保质期那天往前推,知道哪一天没有出售商品,我们就那一天出售它就行了,比如说100 5 ,第五天已经被其他商品占住了,我们就从 4 3 2 1 天里面找一天,看看 哪一天没有出售商品,然后在那一天出售商品。代码:

#include<iostream>
#include<cstdio>
#include<map> 
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=10005;

typedef struct
{
	int val;
	int time;
}point;
point a[maxn];

int vis[maxn];
bool cmp(point a,point b)
{
	return a.val>b.val;
}

int main()
{
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		memset(vis,0,sizeof(vis));
		for(int i=0;i<n;i++)
		{
			scanf("%d%d",&a[i].val,&a[i].time);
		}
		sort(a,a+n,cmp);
		
		int sum=0;
		for(int i=0;i<n;i++)
		{
			if(!vis[a[i].time])
			{
				sum+=a[i].val;
				vis[a[i].time]=1;
			}
			else
			{
				for(int j=a[i].time-1;j>=1;j--)
				{
					if(!vis[j])
					{
						vis[j]=1;
						sum+=a[i].val;
						break;
					}
				}
			}		
		}	
		printf("%d\n",sum);	
		
	}
	return 0;
}

还有一个 并查集的写法:这个写法就有有点难想了,但是思想还是那个思想,我说说一下这个代码是怎么写的,是怎么来想的,首先也是需要一个标记数组(下标也是第几天被占,我们一开始就全部标记为-1)。

现在先说一下这个find 函数,就是下面这个函数,我们一开始将标记数组都设置为-1(一开始都没有被占),然后我们现在就要找一个没有被占的时间,如果pr[x]==-1,我们就选择这个时间了,在这里,我们知道保质期没有0天的,所以我们设置一个参照点就是pre[0]=-1 这个点,(为什么要设置这一个点呢),比如我们一个时间点,被占了的话,那么我们是不是要从 这个天数逐渐减到1天去寻找那个没有被占的时间点,在这个find 函数里面,就是这个原理。来我举一个例子说说这个原理吧 :

                for(int i=0;i<n;i++)
		{
			int t=find(a[i].time);
			if(t>0)
			{
				sum+=a[i].val;
				pre[t]=t-1;
			}	
		}


int find(int x)
{
	return pre[x]==-1?x:pre[x]=find(pre[x]);
	
}


这四个数据  
50 2
30 1
20 2
10 1

一开始
pre[0]=-1  pre[1]=-1 pre[2]=-1 pre[3]=-1  pre[4]=-1
1.先看 50 2这组数据  
判断 pre[2]==-1? 等于-1,我们就把 pre[2]=2-1=1   卖 
 
2.再看 30 1这组数据
判断 pre[1]==-1? 等于-1,我们就把 pre[1]=1-1=0   卖 
 
3.再看 20 2这组数据,pre[2]==1  ->find,->pre[1]=0 ->find->pre[0]==-1 ,返回 0天,因为没有0天 ,不能卖 

4.再看 10 1 这组数据 pre[1]=0  ->find-> pre[0]==-1 ,返回0天 ,因为没有 0天,不能卖 

所以就是上面这个原理,find函数 就相当于一个往上找的函数 ,直到找到那个没有被占的时间点,然后将他的pre[ ]数组标记为上一天,使得能在其他的时间点卖掉。

然后就是我们卖掉一个商品,我们将vis[ 它的保质期 ]赋值为保质期-1, 
 

#include<iostream>
#include<cstdio>
#include<vector>
#include<math.h> 
#include<string.h>
#include<algorithm>
#include<stdlib.h>
typedef long long ll;


using namespace std;
const int maxn=10005;
typedef pair<int,int> p;


typedef struct
{
	int val;
	int time;
}point;

int pre[maxn];
point a[maxn];

int find(int x)
{
	return pre[x]==-1?x:pre[x]=find(pre[x]);
	
}

bool cmp(point a,point b)
{
	return a.val>b.val;
}

int main()
{
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		memset(pre,-1,sizeof(pre));
		for(int i=0;i<n;i++)
		{
			scanf("%d%d",&a[i].val,&a[i].time);
		}
		sort(a,a+n,cmp);
		
		int sum=0;
		for(int i=0;i<n;i++)
		{
			int t=find(a[i].time);
			if(t>0)
			{
				sum+=a[i].val;
				pre[t]=t-1;
			}	
		}
		printf("%d\n",sum);
	}
	return 0;
}
发布了123 篇原创文章 · 获赞 83 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/tsam123/article/details/89406606