week 10

文章包含多个编程题目,涉及欧拉图的特性解决最少笔画问题,递归处理合根植物的计算,Floyd算法求解最短路径问题,并讨论了其他几个与图论相关的算法实现。每个题目都提供了题解思路和代码示例。
摘要由CSDN通过智能技术生成

目录

1.Einstein学画画

题目

输入格式

输出格式

输入输出样例

 题解

代码

2.合根植物

题目

输入格式

输出格式

输入输出样例

题解

代码

3.奶酪

题目

输入格式

输出格式

输入输出样例

题解

代码

4.灾后重建

题目

题目描述

输入格式

输出格式

输入输出样例

 题解

代码

5.聪明的猴子

题目

输入格式

输出格式

输入输出样例

题解

代码


1.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

 题解

根据欧拉图的特性,只用一维数组就能解决。

代码

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

2.合根植物

题目

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

题解

运用递归,层层推进。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll  INF = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double E = exp(1.0);
const int MOD = 1e9+7;
const int MAX = 1e6+5;

int m,n, k ,a,b, pre[MAX];
int Find(int x){
    int r = x;
    while(pre[r] != r) r = pre[r];
    return r;
}

void Union(int x,int y){
    int rx = Find(x);
    int ry = Find(y);
    if(rx != ry)  pre[rx] = ry;
}

int main(){
    cin>>m>>n>>k;
    for(int i = 1; i <= m*n; i++) pre[i] = i;
    for(int i = 0;i<k; i++){
        cin >> a >> b;
        Union(a,b);
    }
    int sum = 0;
    for(int i = 1; i <= m*n; i++){
        if(Find(i) == i) sum++;
    }
    cout<<sum<<endl;

    return 0;
}

3.奶酪

题目

现有一块大奶酪,它的高度为 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

题解

因为n小,直接暴力搜索。

代码

#include<bits/stdc++.h>
using namespace std;
unsigned long long r,n,h,bo,m[1001];
struct dong {
    double x,y,z;
};
dong p[1001];
bool pd(dong a,dong b) {
    long long d;
    d=(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z);
    if(d<=4*r*r) return true;
    else return false;
}
void run(int x) {
    if(bo ==1) return;
    if(p[x].z+r>=h) {
        bo=1;
        return;
    }
    for(int i=1; i<=n; i++) {
        if(m[i]==1) continue;
        if(pd(p[x],p[i])) {
            m[i]=1;
            run(i);
        }
    }
}
int main() {
    int T;
    cin>>T;
    for(int j=1; j<=T; j++) {
        scanf("%d%d%d",&n,&h,&r);
        bo=0;
        for(int i=1; i<=n; i++){
                m[i]=0;
        }
        for(int i=1; i<=n; i++){
                cin>>p[i].x>>p[i].y>>p[i].z;
        }
        for(int i=1; i<=n; i++){
            if(p[i].z<=r){
                m[i]=1;
                run(i);
            }
        }

        if(bo == 1) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

4.灾后重建

题目

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

 题解

floyd最短路径问题, 在两点之间多加一个点来求两点之间最短距离。

代码

#include<bits/stdc++.h>
using namespace std;
#define N 210
int n,m , q, now, a[N], f[N][N];
inline void updata(int k){
	for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(f[i][j] > f[i][k] + f[j][k]) f[i][j]=f[j][i]=f[i][k]+f[j][k];
        }
	}
	return;
}
int main(){
	cin>>n>>m;
	for(int i=0;i<n;i++){
        scanf("%d",a+i);
	}

	for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            f[i][j]=1e9;
        }
	}
	for(int i=0;i<n;i++){
        f[i][i]=0;
	}
	int s1,s2,s3;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&s1,&s2,&s3);
		f[s1][s2] = f[s2][s1] = s3;
	}

	cin>>q;
	for(int i=1;i<=q;i++){
		scanf("%d%d%d",&s1,&s2,&s3);
		while(a[now] <= s3 && now<n){
			updata(now);
			now++;
		}
		if(a[s1]>s3||a[s2]>s3) printf("-1\n");
		else {
			if(f[s1][s2]==1e9) printf("-1\n");
			else cout<<f[s1][s2]<<endl;
		}
	}
	return 0;
}

5.聪明的猴子

题目

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

现在,在这个地区露出水面的有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

题解

采用生成树存图。

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,k,ans, a[1000005][3],b[1000005],pre[1000005];
double sum=-1;

struct node {
	int x,y;
	double p;
}z[1000005];

bool cmp(node k,node d) {
	return k.p<d.p;
}

int Find(int x) {
	if(pre[x] == x) return x;
	return pre[x]=Find(pre[x]);
}

int main(){
	cin>>m;
	for(int i=1; i<=m; i++) cin>>b[i];
	cin>>n;
	for(int i=1; i<=n; i++) cin>>a[i][1]>>a[i][2];
	for(int i=1; i<=n; i++){
		for(int j=1; j<=n; j++){
			if(i != j){
				k++;
				z[k].x=i;
				z[k].y=j;
				z[k].p=sqrt((a[i][1]-a[j][1])*(a[i][1]-a[j][1])+(a[i][2]-a[j][2])*(a[i][2]-a[j][2]));
			}
		}
	}
	int cnt=n;
	sort(z+1,z+k+1,cmp);
	for(int i=1; i<=n; i++) pre[i]=i;
	for(int i=1; i<=k; i++) {
		if(cnt == 1) break;
		int s1=Find(z[i].x),s2=Find(z[i].y);
		if(s1 != s2) {
			pre[s1]=s2;
			cnt--;
			sum=z[i].p;
		}
	}
	for(int i=1; i<=m; i++) {
		if(sum<=b[i]) ans++;
	}
	printf("%d\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值