P1636 Einstein学画画+P8654 [蓝桥杯 2017 国 C] 合根植物+P3958 [NOIP2017 提高组] 奶酪+P1119 灾后重建+P2504 [HAOI2006]聪明的猴子

P1636 Einstein学画画

题目描述

Einstein 学起了画画。

此人比较懒~~,他希望用最少的笔画画出一张画……

给定一个无向图,包含 nn 个顶点(编号 1 \sim n1∼n),mm 条边,求最少用多少笔可以画出图中所有的边。

输入格式

第一行两个整数 n, mn,m。

接下来 mm 行,每行两个数 a, ba,b(a \ne ba=b),表示 a, ba,b 两点之间有一条边相连。

一条边不会被描述多次。

输出格式

一个数,即问题的答案。

输入输出样例

输入 #1复制

5 5
2 3
2 4
2 5
3 4
4 5

输出 #1复制

1

说明/提示

对于 50 \%50% 的数据,n \le 50n≤50,m \le 100m≤100。

对于 100\%100% 的数据,1 \le n \le 10001≤n≤1000,1 \le m \le {10}^51≤m≤105。

题解 P1636 【Einstein学画画】 - ζั͡އއއ๓秋 的博客 - 洛谷博客 (luogu.com.cn)

欧拉路

若从起点到终点的路径恰通过图中每条边一次(起点与终点是不同的点),
则该路径称为欧拉路。
定理1:
存在欧拉路的条件:图是连通的,且存在2个奇点。
如果存在2个奇点,则欧拉路一定是从一个奇点出发,以另一个奇点结束。

找顶点度数为奇数的点的个数,每两个奇点需要画一笔,其中还需要特判奇点数为0的情况

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+3;
int n, m,c[maxn],ans;
int main()
{
	cin >> n >> m;
	int a, b;
	for (int i = 1; i <= m; i++)
	{
		cin >> a >> b;
		++c[a], ++c[b];
	}
	for (int i = 1; i <= n; i++)
	{
		if (c[i] % 2 == 1)
			ans++;
	}
	if (ans == 0)
		cout << 1;
	else
		cout << ans / 2;
	return 0;
}

P8654 [蓝桥杯 2017 国 C] 合根植物

题目描述

w 星球的一个种植园,被分成 m \times nm×n 个小格子(东西方向 mm 行,南北方向 nn 列)。每个格子里种了一株合根植物。

这种植物有个特点,它的根可能会沿着南北或东西方向伸展,从而与另一个格子的植物合成为一体。

如果我们告诉你哪些小格子间出现了连根现象,你能说出这个园中一共有多少株合根植物吗?

输入格式

第一行,两个整数 mm,nn,用空格分开,表示格子的行数、列数(1<m,n<10001<m,n<1000)。

接下来一行,一个整数 kk,表示下面还有 kk 行数据 (0<k<10^5)(0<k<105)。

接下来 kk 行,第行两个整数 aa,bb,表示编号为 aa 的小格子和编号为 bb 的小格子合根了。

格子的编号一行一行,从上到下,从左到右编号。

比如:5 \times 45×4 的小格子,编号:

1  2  3  4
5  6  7  8
9  10 11 12
13 14 15 16
17 18 19 20

输出格式

一行一个整数,表示答案

输入输出样例

输入 #1复制

5 4
16
2 3
1 5
5 9
4 8
7 8
9 10
10 11
11 12
10 14
12 16
14 18
17 18
15 19
19 20
9 13
13 17

输出 #1复制

5

说明/提示

样例解释

时限 1 秒, 256M。蓝桥杯 2017 年第八届国赛

(180条消息) 【蓝桥杯】 历届试题 合根植物(并查集)_酱懵静的博客-CSDN博客

本题其实就是需要求连通子集的个数 --->并查集,(整个图中点与点之间的关系——要么同属于某个点集合,要么各自属于其他的某个点集),注意数据范围

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int n, m, fa[maxn], ans,k,cnt[maxn];
void init(int n)
{
	for (int i = 1; i <= n; i++)fa[i] = i;
}
int getfa(int x) 
{
	return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}
void merge(int x, int y)
{
	fa[getfa(x)] = getfa(y);
}
bool check(int x, int y)
{
	return getfa(x) == getfa(y);
}
int main()
{
	cin >> m >> n>>k;
	init(m * n);//初始化
	int a, b;
	for (int i = 1; i <= k; i++)
	{
		cin >> a >> b;
		merge(a, b);//合并所在集合
	}
	for (int i = 1; i <= m * n; i++)
	{
		cnt[getfa(i)] = 1;//寻找根节点
	}
	for (int i = 1; i <= m * n; i++)
		if (cnt[i])ans += 1;//统计根节点个数
	cout << ans;
}

 P3958 [NOIP2017 提高组] 奶酪

题目背景

NOIP2017 提高组 D2T1

题目描述

现有一块大奶酪,它的高度为 hh,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中,奶酪的下表面为 z = 0z=0,奶酪的上表面为 z = hz=h。

现在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交,Jerry 则可以从奶酪下表面跑进空洞;如果一个空洞与上表面相切或是相交,Jerry 则可以从空洞跑到奶酪上表面。

位于奶酪下表面的 Jerry 想知道,在不破坏奶酪的情况下,能否利用已有的空洞跑 到奶酪的上表面去?

空间内两点 P_1(x_1,y_1,z_1)P1​(x1​,y1​,z1​)、P2(x_2,y_2,z_2)P2(x2​,y2​,z2​) 的距离公式如下:

\mathrm{dist}(P_1,P_2)=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2}dist(P1​,P2​)=(x1​−x2​)2+(y1​−y2​)2+(z1​−z2​)2​

输入格式

每个输入文件包含多组数据。

第一行,包含一个正整数 TT,代表该输入文件中所含的数据组数。

接下来是 TT 组数据,每组数据的格式如下: 第一行包含三个正整数 n,h,rn,h,r,两个数之间以一个空格分开,分别代表奶酪中空洞的数量,奶酪的高度和空洞的半径。

接下来的 nn 行,每行包含三个整数 x,y,zx,y,z,两个数之间以一个空格分开,表示空洞球心坐标为 (x,y,z)(x,y,z)。

输出格式

TT 行,分别对应 TT 组数据的答案,如果在第 ii 组数据中,Jerry 能从下表面跑到上表面,则输出 Yes,如果不能,则输出 No

输入输出样例

输入 #1复制

3 
2 4 1 
0 0 1 
0 0 3 
2 5 1 
0 0 1 
0 0 4 
2 5 2 
0 0 2 
2 0 4

输出 #1复制

Yes
No
Yes

说明/提示

【输入输出样例 11 说明】

第一组数据,由奶酪的剖面图可见:

第一个空洞在 (0,0,0)(0,0,0) 与下表面相切;

第二个空洞在 (0,0,4)(0,0,4) 与上表面相切;

两个空洞在 (0,0,2)(0,0,2) 相切。

输出 Yes

第二组数据,由奶酪的剖面图可见:

两个空洞既不相交也不相切。

输出 No

第三组数据,由奶酪的剖面图可见:

两个空洞相交,且与上下表面相切或相交。

输出 Yes

【数据规模与约定】

对于 20\%20% 的数据,n = 1n=1,1 \le h1≤h,r \le 10^4r≤104,坐标的绝对值不超过 10^4104。

对于 40\%40% 的数据,1 \le n \le 81≤n≤8,1 \le h1≤h,r \le 10^4r≤104,坐标的绝对值不超过 10^4104。

对于 80\%80% 的数据,1 \le n \le 10^31≤n≤103,1 \le h , r \le 10^41≤h,r≤104,坐标的绝对值不超过 10^4104。

对于 100\%100% 的数据,1 \le n \le 1\times 10^31≤n≤1×103,1 \le h , r \le 10^91≤h,r≤109,T \le 20T≤20,坐标的绝对值不超过 10^9109。

题解 P3958 【奶酪】 - HorizonWind 的博客 - 洛谷博客 (luogu.com.cn)

判断每一条通道是否存在元素与底部、顶部相连即可。如果都有,那么输出 Yes。这个可以使用并查集。

有多组数据,定义全局变量的时候需要注意......

答案的Yes和No只有首字母是大写的!!

也可以使用搜索来解答

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 3;
int n, t, fa[maxn];
long long x[maxn], y[maxn], z[maxn],r,h;
void init(int n)
{
	for (int i = 1; i <= n; i++)fa[i] = i;
}
int getfa(int x)
{
	return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}
void merge(int x, int y)
{
	fa[getfa(x)] = getfa(y);
}
long long dis(long long x, long long y, long long z, long long x1, long long y1, long long z1) {
	return(x - x1) * (x - x1) + (y - y1) * (y - y1) + (z - z1) * (z - z1);
}//两点距离平方的公式
int main()
{
	cin >> t;
	for (int i = 1; i <= t; i++) {
		cin >> n >> h >> r;
		int cnt1[maxn], cnt2[maxn], c1=0, c2=0;
		init(n);
		for (int j = 1; j <= n; j++) {
			cin >> x[j] >> y[j] >> z[j];
			if (z[j] + r >= h)c1++, cnt1[c1] = j;
			if (z[j] - r <= 0)c2++, cnt2[c2] = j;
			for (int k = 1; k <= j; k++) {
				if ((x[j] - x[k]) * (x[j] - x[k]) + (y[j] - y[k]) * (y[j] - y[k]) > 4 * r * r) continue;
				if (dis(x[j], y[j], z[j], x[k], y[k], z[k]) <= 4 * r * r)
				{
					merge(j, k);
				}
			}
		}
		int flag = 0;
		for (int j = 1; j <= c1; j++) {
			for (int k = 1; k <= c2; k++) {
				if (getfa(cnt1[j]) == getfa(cnt2[k]))
				{
					flag = 1;
					break;
				}
			}
			if (flag)break;
		}
		if(flag==1)cout << "Yes" << endl;
		else cout << "No" << endl;
	}
}

 P1119 灾后重建

题目背景

B 地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响。但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车。换句话说,只有连接着两个重建完成的村庄的公路才能通车,只能到达重建完成的村庄。

题目描述

给出 B 地区的村庄数 NN,村庄编号从 00 到 N-1N−1,和所有 MM 条公路的长度,公路是双向的。并给出第 ii 个村庄重建完成的时间 t_iti​,你可以认为是同时开始重建并在第 t_iti​ 天重建完成,并且在当天即可通车。若 t_iti​ 为 00 则说明地震未对此地区造成损坏,一开始就可以通车。之后有 QQ 个询问 (x,y,t)(x,y,t),对于每个询问你要回答在第 tt 天,从村庄 xx 到村庄 yy 的最短路径长度为多少。如果无法找到从 xx 村庄到 yy 村庄的路径,经过若干个已重建完成的村庄,或者村庄 xx 或村庄 yy 在第 tt 天仍未重建完成,则需要返回 -1

输入格式

第一行包含两个正整数N,MN,M,表示了村庄的数目与公路的数量。

第二行包含NN个非负整数t_0, t_1,…, t_{N-1}t0​,t1​,…,tN−1​,表示了每个村庄重建完成的时间,数据保证了t_0 ≤ t_1 ≤ … ≤ t_{N-1}t0​≤t1​≤…≤tN−1​。

接下来MM行,每行33个非负整数i, j, wi,j,w,ww为不超过1000010000的正整数,表示了有一条连接村庄ii与村庄jj的道路,长度为ww,保证i≠ji=j,且对于任意一对村庄只会存在一条道路。

接下来一行也就是M+3M+3行包含一个正整数QQ,表示QQ个询问。

接下来QQ行,每行33个非负整数x, y, tx,y,t,询问在第tt天,从村庄xx到村庄yy的最短路径长度为多少,数据保证了tt是不下降的。

输出格式

共QQ行,对每一个询问(x, y, t)(x,y,t)输出对应的答案,即在第tt天,从村庄xx到村庄yy的最短路径长度为多少。如果在第t天无法找到从xx村庄到yy村庄的路径,经过若干个已重建完成的村庄,或者村庄x或村庄yy在第tt天仍未修复完成,则输出-1−1。

输入输出样例

输入 #1复制

4 5
1 2 3 4
0 2 1
2 3 1
3 1 2
2 1 4
0 3 5
4
2 0 2
0 1 2
0 1 3
0 1 4

输出 #1复制

-1
-1
5
4

说明/提示

对于30\%30%的数据,有N≤50N≤50;

对于30\%30%的数据,有t_i= 0ti​=0,其中有20\%20%的数据有t_i = 0ti​=0且N>50N>50;

对于50\%50%的数据,有Q≤100Q≤100;

对于100\%100%的数据,有N≤200N≤200,M≤N \times (N-1)/2M≤N×(N−1)/2,Q≤50000Q≤50000,所有输入数据涉及整数均不超过100000100000。

题解 P1119 【灾后重建】 - Time_Rune 的博客 - 洛谷博客 (luogu.com.cn)

注意题目中的时间都是按照先后顺序给出的,使用Floyd算法依次更新前k个节点最短路,(若对于每一次询问,都把小于当前时间的点和边加入到图中,然后每一次询问都跑一边Floyed,这样做肯定会超时)

#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int m, n,f[203][203],q,a[203],pos;
int main()
{
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		f[i][i] = 0;
		for (int j = 0; j < n; j++)
			f[i][j] = INF;
	}
	for (int i = 0; i < n; i++)cin >> a[i];
	for (int i = 0; i < m; i++)
	{
		int x, y, w;
		cin >> x >> y >> w;
		f[x][y] = f[y][x] = w;
	}
	cin >> q;
	for (int i = 0; i < q; i++)
	{
		int x, y, t;
		cin >> x >> y >> t;
		while (pos < n&& a[pos] <= t)//满足时间条件,加入该村庄更新最短路径
		{
			for (int j = 0; j < n; j++) {//floyed
				for (int k = 0; k < n; k++){
					f[j][k] = f[k][j] = min(f[j][k], f[j][pos] + f[pos][k]);//无向图,两边都要更新最短路径
				}
			}
			pos++;
		}
		if (a[x] > t || a[y] > t || f[x][y] == INF)cout << -1 << endl;//村庄未重建完成或者没有路径
		else cout << f[x][y] << endl;
	}
	return 0;
}

P2504 [HAOI2006]聪明的猴子 

题目描述

在一个热带雨林中生存着一群猴子,它们以树上的果子为生。昨天下了一场大雨,现在雨过天晴,但整个雨林的地表还是被大水淹没着,部分植物的树冠露在水面上。猴子不会游泳,但跳跃能力比较强,它们仍然可以在露出水面的不同树冠上来回穿梭,以找到喜欢吃的果实。

现在,在这个地区露出水面的有N棵树,假设每棵树本身的直径都很小,可以忽略不计。我们在这块区域上建立直角坐标系,则每一棵树的位置由其所对应的坐标表示(任意两棵树的坐标都不相同)。

在这个地区住着的猴子有M个,下雨时,它们都躲到了茂密高大的树冠中,没有被大水冲走。由于各个猴子的年龄不同、身体素质不同,它们跳跃的能力不同。有的猴子跳跃的距离比较远(当然也可以跳到较近的树上),而有些猴子跳跃的距离就比较近。这些猴子非常聪明,它们通过目测就可以准确地判断出自己能否跳到对面的树上。

【问题】现已知猴子的数量及每一个猴子的最大跳跃距离,还知道露出水面的每一棵树的坐标,你的任务是统计有多少个猴子可以在这个地区露出水面的所有树冠上觅食。

输入格式

输入文件monkey.in包括:

第1行为一个整数,表示猴子的个数M(2<=M<=500);

第2行为M个整数,依次表示猴子的最大跳跃距离(每个整数值在1--1000之间);

第3行为一个整数表示树的总棵数N(2<=N<=1000);

第4行至第N+3行为N棵树的坐标(横纵坐标均为整数,范围为:-1000--1000)。

(同一行的整数间用空格分开)

输出格式

输出文件monkey.out包括一个整数,表示可以在这个地区的所有树冠上觅食的猴子数。

输入输出样例

输入 #1复制

4
 1 2 3 4
6
0 0
1 0
1 2
-1 -1
-2 0
2 2

输出 #1复制

3

说明/提示

【数据规模】

对于40%的数据,保证有2<=N <=100,1<=M<=100

对于全部的数据,保证有2<=N <= 1000,1<=M=500

感谢@charlie003 修正数据

 

#include <bits/stdc++.h>
using namespace std;
double nx[1003], ny[1003],maxn =-1; 
int n, m, num[501],pos,ans,cnt,fa[1003];
struct node {
	int x, y;
	double dis;
}e[1000010];

bool cmp(node a, node b) { 
	return a.dis < b.dis;
}
int getfa(int x)
{
	return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}
void merge(int x, int y)
{
	fa[getfa(x)] = getfa(y);
}
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin>>num[i];
	cin >> m;
	for (int i = 1; i <= m; i++) cin>>nx[i], cin>>ny[i];
	for (int i = 1; i <= m; i++)
		for (int j = i + 1; j <= m; j++) {
			e[++cnt].x = i;
			e[cnt].y = j;
			e[cnt].dis = sqrt((nx[i] - nx[j]) * (nx[i] - nx[j]) + (ny[i] - ny[j]) * (ny[i] - ny[j]));
		}
	sort(e + 1, e + cnt + 1, cmp);
	for (int i = 1; i <= m; i++) fa[i] = i;
	for (int i = 1; i <= cnt; i++) {
		if (getfa(e[i].x) != getfa(e[i].y)) {
			merge(e[i].x, e[i].y);
			pos++;
			maxn = max(maxn, e[i].dis);
		}
		if (pos == m - 1) break;
	}
	for (int i = 1; i <= n; i++) if (num[i] >= maxn) ans++;
	cout << ans;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值