程序设计思维与实践 Week6 作业

A - 氪金带东

题意

实验室里原先有一台电脑(编号为1),最近氪金带师咕咕东又为实验室购置了N-1台电脑,编号为2到N。每台电脑都用网线连接到一台先前安装的电脑上。但是咕咕东担心网速太慢,他希望知道第i台电脑到其他电脑的最大网线长度,但是可怜的咕咕东在不久前刚刚遭受了宇宙射线的降智打击,请你帮帮他。
在这里插入图片描述
提示: 样例输入对应这个图,从这个图中你可以看出,距离1号电脑最远的电脑是4号电脑,他们之间的距离是3。 4号电脑与5号电脑都是距离2号电脑最远的点,故其答案是2。5号电脑距离3号电脑最远,故对于3号电脑来说它的答案是3。同样的我们可以计算出4号电脑和5号电脑的答案是4.

解题思路

  • 本题电脑之间的连接是一个树的结构,而本题需要求取树上任意一点到其他点的最长距离。而树恰好有一条性质适合本题使用:距离任意点最远的点一定是直径的一个端点。
  • 那么我们可以先找到树的直径的两个端点,再计算树上任意一点到两端点的距离,即可获得答案。
  • 求取树的直径两端点,可以先从树上任意一点开始遍历,找到距离最远的点即为直径的一个端点,再从这个点遍历,最远的即为直径的另一个端点。

代码

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
struct edge{
	int u,w;
};
vector<edge> graph[10005];
int p1[10005],p2[10005];
bool r[10005];
int v,vs;
void dfs1(int i, int s, int* p){
	bool k = 0;
	r[i] = true;
	if(p!=nullptr)
		p[i] = s;
	for(unsigned j = 0; j < graph[i].size(); j++){
		if(r[graph[i][j].u] == false){
			k = 1;
			dfs1(graph[i][j].u, s + graph[i][j].w, p);
		}
	}
	if(k == 0){
		if(s > vs){
			v = i;
			vs = s;
		}
		return;
	}
}
int main()
{
	int n;
	while(cin >> n){
		for(int i = 0; i <= n+4; i++){
			graph[i].clear();
			p1[i] = 0;
			p2[i] = 0;
		}
		for(int i = 2; i <= n; i++){
			int t1, t2;
			edge temp;
			cin >> t1 >> t2;
			temp.u = t1;
			temp.w = t2;
			graph[i].push_back(temp);
			temp.u = i;
			graph[t1].push_back(temp);
		}
		memset(r, 0, sizeof(r));
		v = 0; vs = 0;
		dfs1(1,0,nullptr);
		memset(r,0,sizeof(r));
		int tv = v; v = 0; vs = 0;
		dfs1(tv,0,p1);
		memset(r,0,sizeof(r));
		tv = v; v = 0; vs = 0;
		dfs1(tv,0,p2);
		for(int i = 1; i <= n; ++i){
			if(p1[i] > p2[i])
				cout << p1[i] << "\n";
			else
				cout << p2[i] << "\n";
		}
	}
	return 0;
}

B - 戴好口罩!

题意

给定 n n 名学生, m m 个群体,学生0受到感染,找出所有需要隔离的学生。

解题思路

用并查集将所有互相接触过的学生加入一个子集中,最后找出学生0所在子集输出答案即可。

代码

#include <iostream>
using namespace std;
int f[100005];
int find(int k){
    if (f[k] == k) return k;
    return f[k] = find(f[k]);
}
void init(){
	for(int i = 0; i < 100005; i++)
		f[i] = i;
}
int main()
{
	int n, m;
	while(cin >> n >> m){
		if(n == 0 && m == 0) break;
		init();
		while(m--){
			int num;
			cin >> num;
			int t1, t2;
			cin >> t1;
			num--;
			while(num--){
				cin >> t2;
				if(find(t2) != find(t1))
					f[find(t2)] = find(t1);
			}
		}
		int ans = 0;
		for(int i = 0; i < n; i++){
			if(find(i) == find(0))
				ans++;
		}
		cout << ans << endl;
	}
	return 0;
}

C - 掌握魔法の东东 I

题意

东东在老家农村无聊,想种田。农田有 n n 块,编号从 1 1 ~ n n 。种田要灌氵
众所周知东东是一个魔法师,他可以消耗一定的 MP 在一块田上施展魔法,使得黄河之水天上来。他也可以消耗一定的 MP 在两块田的渠上建立传送门,使得这块田引用那块有水的田的水。 ( 1 n 3 e 2 1\leq n\leq 3e2 )
黄河之水天上来的消耗是 W i W_i i i 是农田编号 ( 1 W i 1 e 5 1\leq W_i\leq 1e5 )
建立传送门的消耗是 P i j P_{ij} i j i、j 是农田编号 ( 1 P i j 1 e 5 , P i j = P j i , P i i = 0 ) (1\leq P_{ij} \leq 1e5, P_{ij} = P_{ji}, P_{ii} =0)
东东为所有的田灌氵的最小消耗

解题思路

  • 本题可以看做 n n 块农田加上天一共 n + 1 n+1 个点的最小生成树问题。
  • 天这个点和任何一个农田都有边。
  • 向边集中添加边时注意只需添加矩阵的一半,最后跑一下Kruskal即可。

代码

#include <iostream>
#include <algorithm>
using namespace std;
int f[310];
int k, n, ans;
struct edge{
	int u, v, w;
}e[200000];
void make_edge(int u, int v, int w, int i){
	e[i].u = u;
	e[i].v = v;
	e[i].w = w;
}
int find(int k){
    if (f[k] == k) return k;
    return f[k] = find(f[k]);
}
bool cmp(edge a, edge b){
	return a.w < b.w;
}
void kruskal(){
	int cnt = 0;
	for(int i = 0; i < k; i++){
		if(find(e[i].u) != find(e[i].v)){
			f[find(e[i].u)] = find(e[i].v);
			ans += e[i].w;
			cnt++;
		}
		if(cnt == n)
			break;
	}
}
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++){
		f[i] = i;
		int weight;
		cin >> weight;
		make_edge(0,i,weight,k++);
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++){
			int temp;
			cin >> temp;
			if(j > i)
				make_edge(i,j,temp,k++);
		}
	}
	sort(e, e + k, cmp);
	kruskal();
	cout << ans << endl;
	return 0;
}

D - 数据中心

题意

在这里插入图片描述

解题思路

  • 不知道是题面出错了还是什么,就是道裸的最小生成树,记录一下最后加入生成树的边即可。
  • 光看题目描述,让我求root到任意一点的最长距离最短的生成树?这怎么做嘛。
  • 看了眼样例,emmmmm?和想象中的题目不一样,好像就是个裸的最小生成树?
  • 交了一发A了…?黑人问号

代码

#include <iostream>
#include <algorithm>
using namespace std;
int f[50005];
int k, n, ans;
struct edge{
	int u, v, w;
}e[100005];
void make_edge(int u, int v, int w, int i){
	e[i].u = u;
	e[i].v = v;
	e[i].w = w;
}
int find(int k){
    if (f[k] == k) return k;
    return f[k] = find(f[k]);
}
bool cmp(edge a, edge b){
	return a.w < b.w;
}
int kruskal(){
	int cnt = 0;
	for(int i = 0; i < k; i++){
		if(find(e[i].u) != find(e[i].v)){
			f[find(e[i].u)] = find(e[i].v);
			ans += e[i].w;
			cnt++;
		}
		if(cnt == n - 1){
			return i;
		}
	}
	return k-1;
}
int main()
{
	int m, root;
	cin >> n >> m >> root;
	for(int i = 1; i <= n; i++)
		f[i] = i;
	while(m--){
		int u,v,w;
		cin >> u >> v >> w;
		make_edge(u,v,w,k++);
	}
	sort(e, e + k, cmp);
	cout << e[kruskal()].w << endl;
	return 0;
}
发布了12 篇原创文章 · 获赞 0 · 访问量 249

猜你喜欢

转载自blog.csdn.net/weixin_43851185/article/details/105164051
今日推荐