1月6日自习总结

刷题

朋友

朋友 - 洛谷

题目背景

小明在 A 公司工作,小红在 B 公司工作。

题目描述

这两个公司的员工有一个特点:一个公司的员工都是同性。

A 公司有 NN 名员工,其中有 PP 对朋友关系。B 公司有 MM 名员工,其中有 QQ 对朋友关系。朋友的朋友一定还是朋友。

每对朋友关系用两个整数 (Xi,Yi)(Xi​,Yi​) 组成,表示朋友的编号分别为 Xi,YiXi​,Yi​。男人的编号是正数,女人的编号是负数。小明的编号是 11,小红的编号是 −1−1。

大家都知道,小明和小红是朋友,那么,请你写一个程序求出两公司之间,通过小明和小红认识的人最多一共能配成多少对情侣(包括他们自己)。

输入格式

输入的第一行,包含 44 个空格隔开的正整数 N,M,P,QN,M,P,Q。

之后 PP 行,每行两个正整数 Xi,YiXi​,Yi​。

之后 QQ 行,每行两个负整数 Xi,YiXi​,Yi​。

输出格式

输出一行一个正整数,表示通过小明和小红认识的人最多一共能配成多少对情侣(包括他们自己)。

输入输出样例

输入 #1

4 3 4 2
1 1
1 2
2 3
1 3
-1 -2
-3 -3

输出 #1

2

说明/提示

对于 30%30% 的数据,N,M≤100N,M≤100,P,Q≤200P,Q≤200;

对于 80%80% 的数据,N,M≤4×103N,M≤4×103,P,Q≤104P,Q≤104;

对于 100%100% 的数据,N,M≤104N,M≤104,P,Q≤2×104P,Q≤2×104。

算法:并查集

思路

求通过小红和小明认识的人最多一共能配成多少对情侣,即求小红和小明认识的人那个 最少,最少是多少人。用两个并查集,将小红和小明认识的人分别放在一颗树上,然后再算树上有多少结点就是认识有多少人

#include<stdio.h>
int min(int x, int y) {
	return x > y ? y : x;
}
int a[11110], b[11110];//数组一定要开大一点,我就是只看了对于30%的数据只开了110大小的数组,导致我一直RE30分,一直找不到错在哪
int n, m;
void inti(int nn, int *arr) {//并查集初始化函数
	for (int i = 1; i <= nn; i++) {
		arr[i] = i;
	}
}
int find(int x, int *arr) {//祖先查找函数
	return arr[x] == x ? x : arr[x] = find(arr[x], arr);//这样子写能加快程序运行速度
}
void uniun(int x, int y, int *arr) {//合并函数,将x祖先的祖先改为y的祖先
	arr[find(x, arr)] = find(y, arr);
}
int main() {
	int p, q;
	scanf("%d %d %d %d", &n, &m, &p, &q);
	inti(n, a);
	inti(m, b);
	for (int i = 0; i < p; i++) {//小明公司
		int x, y;
		scanf("%d %d", &x, &y);
		uniun(x, y, a);
	}
	int man = 0;
	for (int i = 1; i <= n; i++) {//根结点相同就是在同一颗树上,就是和小明相互认识
		if (find(i, a) == find(1, a)) {
			man++;
		}
	}
	for (int i = 0; i < q; i++) {
		int x, y;
		scanf("%d %d", &x, &y);
		uniun(-x, -y, b);
	}
	int woman = 0;
	for (int i = 1; i <= m; i++) {
		if (find(i, b) == find(1, b)) {
			woman++;
		}
	}
	printf("%d", min(man, woman));//找最小并打印
	return 0;
}

搭配购买

搭配购买 - 洛谷

题目描述

明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有 nn 朵云,云朵已经被老板编号为 1,2,3,...,n1,2,3,...,n,并且每朵云都有一个价值,但是商店的老板是个很奇怪的人,他会告诉你一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买,电脑组的你觉得这礼物实在是太新奇了,但是你的钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云。

输入格式

第一行输入三个整数,n,m,wn,m,w,表示有 nn 朵云,mm 个搭配和你现有的钱的数目。

第二行至 n+1n+1 行,每行有两个整数, ci,dici​,di​,表示第 ii 朵云的价钱和价值。

第 n+2n+2 至 n+1+mn+1+m 行 ,每行有两个整数 ui,viui​,vi​。表示买第 uiui​ 朵云就必须买第 vivi​ 朵云,同理,如果买第 vivi​ 朵就必须买第 uiui​ 朵。

输出格式

一行,表示可以获得的最大价值。

输入输出样例

输入 #1

5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2

输出 #1

1

说明/提示

  • 对于 30%30% 的数据,满足 1≤n≤1001≤n≤100;
  • 对于 50%50% 的数据,满足 1≤n,w≤1031≤n,w≤103,1≤m≤1001≤m≤100;
  • 对于 100%100% 的数据,满足 1≤n,w≤1041≤n,w≤104,0≤m≤5×1030≤m≤5×103。

 算法:

并查集,0/1背包问题

思路:

将所有要搭配在一起的云看做一颗树,每棵树的所有云的价值之和看做物体的价值,价钱之和看做物体的体积,现有的钱看做背包的容量,求最大价值即求能装入背包的物体的价值之和最大。

#include<stdio.h>
#include<stdlib.h>
struct yun {//一朵云
	int price;//该多云的价钱
	int value;//该朵云的价值
	int ancestor = 0; //与其搭配的云的编号
} all[12000];
struct yuns { //一团云
	int allprice;//该团云总价钱
	int allvalue;//该团云总价值
} al[12000];
int la = 0;
int n, m, w;
void inti(int n) {//数组初始化
	for (int i = 1; i <= n; i++) {
		all[i].ancestor = i;
	}
}
int find(int n) { //查找根结点
	return all[n].ancestor == n ? n : all[n].ancestor = find(all[n].ancestor);
}
void defind(int x) { //x为要统计的根结点的标号
	int priceall = 0, valueall = 0;
	for (int i = 1; i <= n; i++) {
		if (all[i].ancestor == x) {
			priceall += all[i].price;
			valueall += all[i].value;
		}
	}
	al[la].allprice = priceall;
	al[la].allvalue = valueall;
	la++;
}
void unionk(int x, int y) { //搭配合并起来
	all[find(x)].ancestor = find(y);
}
int dp[12000] = {0};
int max(int x, int y) {
	return x > y ? x : y;
}
int main() {
	scanf("%d %d %d", &n, &m, &w);
	inti(n);
	for (int i = 1; i <= n; i++) { //记录价钱和价值
		scanf("%d %d", &all[i].price, &all[i].value);
	}
	for (int i = 0; i < m; i++) {//记录搭配
		int x, y;
		scanf("%d %d", &x, &y);
		unionk(x, y);
	}
	for (int i = 1; i <= n; i++) { //给各朵云找最大祖先
		all[i].ancestor = find(all[i].ancestor);
	}
	for (int i = 1; i <= n; i++) {
		if (all[i].ancestor == i) { //找根结点
			defind(i);//根据根结点去找在这颗树上的所有云;
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int li = w; li >=al[i].allprice; li--) {
			dp[li] = max(dp[li],dp[li-al[i].allprice]+al[i].allvalue);
		}
	}
	printf("%d",dp[w]);
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值