并查集专题

题面:

B-supermarket
A supermarket has a set Prod of products on sale. It earns a profit px for each product x∈Prod sold by a deadline dx that is measured as an integral number of time units starting from the moment the sale begins. Each product takes precisely one unit of time for being sold. A selling schedule is an ordered subset of products Sell ≤ Prod such that the selling of each product x∈Sell, according to the ordering of Sell, completes before the deadline dx or just when dx expires. The profit of the selling schedule is Profit(Sell)=Σ(x∈Sell)px. An optimal selling schedule is a schedule with a maximum profit.
For example, consider the products Prod={a,b,c,d} with (pa,da)=(50,2), (pb,db)=(10,1), (pc,dc)=(20,2), and (pd,dd)=(30,1). The possible selling schedules are listed in table 1. For instance, the schedule Sell={d,a} shows that the selling of product d starts at time 0 and ends at time 1, while the selling of product a starts at time 1 and ends at time 2. Each of these products is sold by its deadline. Sell is the optimal schedule and its profit is 80.
在这里插入图片描述
Write a program that reads sets of products from an input text file and computes the profit of an optimal selling schedule for each set of products.
Input
A set of products starts with an integer 0 <= n <= 10000, which is the number of products in the set, and continues with n pairs pi di of integers, 1 <= pi <= 10000 and 1 <= di <= 10000, that designate the profit and the selling deadline of the i-th product. White spaces can occur freely in input. Input data terminate with an end of file and are guaranteed correct.
Output
For each set of products, the program prints on the standard output the profit of an optimal selling schedule for the set. Each result is printed from the beginning of a separate line.
本题其实我一开始没想到用并查集来做,最主要原因就是没有读清题面含义(锅全甩给英文题面233),本题是要求利益最大化,也就是本题是以利益为作为主要变量来构思,而不是天数(只要能把商品在保质期内卖完就行),既然是以商品的利益最大化为标准那么在相同期限内,就可以引入一个很好的并查集思想,那就是以天数(当然需要用到结构体和快排)作为参数进行并集和查集,主要流程就是我现在定义一个结构体,这个结构体里面包含了所有商品的性质(包括保质期,包括利益)题目要求利益的最大化,所以以利益作为参数,从前往后sort排序(值得注意的是:C++算法库里面sort函数默认的是从低到高依次排序,因此需要引入一个新的改变顺序的函数作为sort函数的参数),下面重点来了:本题的并查集的思想是:第几天卖什么东西(题面规定一天只能卖出一件商品),我作为无良奸商,我最想卖的是收益最大的,但是为了能卖出更多的产品我只能让这个商品在保质期的最后一天卖出就是所有的保质期天数相同的产品并集为一棵树,卖出这棵树上的任何产品后,这棵树与其保质期-1的那棵树合并,(例如我在第四天卖了收益最高的东西,那和它一一昂保质期只有四天的东西,就只能挑在前三天卖出,这样就和保质期只有三天的树合并)再继续查找,周而复始,最终使得所有商品全部过期卖不出去为止(其实用一个for循环遍历所有商品即可)。

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=10000000;
int a[N];
int find(int x){
	if(a[x]!=x) a[x]=find(a[x]);
	return a[x];
}
struct  shop{
	int px,dx;
}s[N];
int cmp(struct shop x,struct shop y){
	return x.px>y.px;
}
int main(){
	int d,sum=0,k=0,t=0;
	while(~scanf("%d",&d)){
		for(int i=1;i<=d;i++)	scanf("%d %d",&s[i].px,&s[i].dx);
		sort(s+1,s+d+1,cmp);
		sum=0;
		for(int i=1;i<=N;i++) a[i]=i;
		
		for(int i=1;i<=d;i++){
			t=find(s[i].dx);
			if(t>0){
				a[t]=t-1;//注意这里:这里保留a[t],别写成其他的!  
				sum+=s[i].px;
			}
			}
			printf("%d\n",sum);}
		return 0;
}

C-食物链
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
Input
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output
只有一个整数,表示假话的数目。

食物链这道题可谓带权并查集中思维量比较大的一道,维护并查集的节点距离的过程相对来说比较难理解
这道题分为两种情况:1.新关系的引入(之前的输入没有涉及到的关系)
2.旧关系的判断(之前的输入涉及到的东西来判断当然输入是否符合旧关系)
这就要用到并查集
(1) 旧关系的判断
如果根节点(祖宗)是同一个,那么这就是之前引入的旧关系,那么就直接判断,我现在想要的关系和我维护的旧关系一样,那就是旧关系。
(2) 新关系的引入
如果根节点(祖宗)不是同一个,那么这是我之前从未涉及到的新关系,那么需要并集并维护节点性质。
代码解释:
在这里插入图片描述
X对y的关系已知,已维护x到根节点的距离、y到根节点的距离
ar[x]+α=relation+ar[y];
因此我们不难解释

a[find(zuo)]=find(you);//并集
ar[findz]=(r-1+ar[you]-ar[zuo])%3;//计算新建px和py的关系(就只有这个的一段关系)

那么对于find函数来说
对于两个物种之间的关系,
int find(int x){
	if(x!=a[x]){
		int t=a[x];
		a[x]=find(a[x]);//先路径压缩,然后逐个判断和维护与根节点的距离
		ar[x]=(ar[x]+ar[t])%3;//ar[t]是每一段的长度求出关系累和算出ar[x]最终就是和根节点的距离 
	}
	return a[x];
}

//带权并查集 
//魔鬼食物链
#include<stdio.h>
#include<iostream>
using namespace std;
int a[10000000];
int ar[100000000];
int find(int x){
	if(x!=a[x]){
		int t=a[x];
		a[x]=find(a[x]);
		ar[x]=(ar[x]+ar[t])%3;//这个的意思是:先路径压缩,再相加,ar[t]是每一段的长度求出关系 
	}
	return a[x];
}
int main(){
	int num,n,r,zuo,you,sum=0;
	scanf("%d %d",&num,&n);
	//并查集的初始化
	for(int i=1;i<=num;i++) a[i]=i;
	while(n--){
		scanf("%d %d %d",&r,&zuo,&you);
		if(zuo>num||you>num||(r==2&&zuo==you)) sum++;
		//特判特例情况让它不成立
		//就是最根本的逻辑错误 
		else {
			int findz=find(zuo);
			int findy=find(you);
			if(findz==findy){
				if(r-1!=((ar[zuo]-ar[you]+3)%3)) sum++;
				//就是说如果我求出来的(已知)关系不等于我判断的关系
				//那么这个就算是一个错误的解	 
			}else{
				a[find(zuo)]=find(you);
				//根节点下面的吃上面的
				ar[findz]=(r-1+ar[you]-ar[zuo])%3;//ar you和zuo代表的是和父节点的距离! 
				//合并新树,给原先的根节点,赋了一个性质 
			} 
		}
	}printf("%d\n",sum);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值