第一题
氪金带东
实验室里原先有一台电脑(编号为1),最近氪金带师咕咕东又为实验室购置了N-1台电脑,编号为2到N。每台电脑都用网线连接到一台先前安装的电脑上。但是咕咕东担心网速太慢,他希望知道第i台电脑到其他电脑的最大网线长度,但是可怜的咕咕东在不久前刚刚遭受了宇宙射线的降智打击,请你帮帮他。
提示: 样例输入对应这个图,从这个图中你可以看出,距离1号电脑最远的电脑是4号电脑,他们之间的距离是3。 4号电脑与5号电脑都是距离2号电脑最远的点,故其答案是2。5号电脑距离3号电脑最远,故对于3号电脑来说它的答案是3。同样的我们可以计算出4号电脑和5号电脑的答案是4.
Input
输入文件包含多组测试数据。对于每组测试数据,第一行一个整数N (N<=10000),接下来有N-1行,每一行两个数,对于第i行的两个数,它们表示与i号电脑连接的电脑编号以及它们之间网线的长度。网线的总长度不会超过10^9,每个数之间用一个空格隔开。
Output
对于每组测试数据输出N行,第i行表示i号电脑的答案 (1<=i<=N).
Example
- input
5
1 1
2 1
3 1
1 1
0 - output
3
2
3
4
4
解题思路
在一棵树中,从任意一点开始,与之距离最长的两个点就是整棵树距离最长的两个点,反之,整棵树距离最长的两个点也是任意一点到整棵树其他点中距离最长的两点.我们首先通过两次dfs找到这最远距离的两点中的一个(假设这个点为v),遍历过程中记录各点到起点的距离,而后从v再次dfs找到另一个端点并且记录v到各点距离,最后从另一个端点dfs同时记录到各点距离。最后我们从第一个点开始遍历,输出到两个端点距离中的最大值即可。
代码实现
#include<iostream>
#include<cstring>
using namespace std;
struct edge {
int beg, end, weight, connect;
};
edge e[100000];
int start[100000], index,s;
int d1[100000], d2[100000],vis[100000];
void Add(int u, int v, int w) {
e[index].beg = u; e[index].end = v; e[index].weight = w;
e[index].connect = start[u];
start[u] = index++;
e[index].beg = v; e[index].end = u; e[index].weight = w;
e[index].connect = start[v];
start[v] = index++;
}
void dfs(int *d,int u, int len) {
if (d[s] < len) s = u;
d[u] = len;
for (int i = start[u]; i != -1; i = e[i].connect) {
int v = e[i].end;
if (!vis[v]) {
vis[v] = true;
dfs(d, v, len + e[i].weight);
}
}
}
void deal(int *d,int s)
{
memset(vis, 0, sizeof(vis));
d[s] = 0; vis[s] = 1;
dfs(d, s, 0);
}
int main() {
ios::sync_with_stdio(false);
int n;
while (cin >> n) {
index = 0;
memset(start, -1, sizeof(start));
for (int i = 2; i <= n; i++)
{
int v,w;
cin >> v >> w;
Add(i, v, w);
}
deal(d1, 1);
deal(d1, s);
deal(d2, s);
for (int i = 1; i <= n; i++)
cout << max(d1[i], d2[i]) << endl;
}
}
第二题
戴好口罩
新型冠状病毒肺炎(Corona Virus Disease 2019,COVID-19),简称“新冠肺炎”,是指2019新型冠状病毒感染导致的肺炎。
如果一个感染者走入一个群体,那么这个群体需要被隔离!
小A同学被确诊为新冠感染,并且没有戴口罩!!!!!!
危!!!
时间紧迫!!!!
需要尽快找到所有和小A同学直接或者间接接触过的同学,将他们隔离,防止更大范围的扩散。
众所周知,学生的交际可能是分小团体的,一位学生可能同时参与多个小团体内。
请你编写程序解决!戴口罩!!
Input
多组数据,对于每组测试数据:
第一行为两个整数n和m(n = m = 0表示输入结束,不需要处理),n是学生的数量,m是学生群体的数量。0 < n <= 3e4 , 0 <= m <= 5e2
学生编号为0~n-1
小A编号为0
随后,m行,每行有一个整数num即小团体人员数量。随后有num个整数代表这个小团体的学生。
Output
输出要隔离的人数,每组数据的答案输出占一行
Example
- input
100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0 - output
4
1
1
解题思路
这个题可以用并查集来解决,将每个团体所有成员都加入一个并查集,最后病人所在的并查集检查即可
#include<iostream>
using namespace std;
int n, m;
int stdnt[30005], judge[30005];
int find(int x) {
if (stdnt[x] == x) return x;
else return stdnt[x] = find(stdnt[x]);
}
void unite(int x, int y) {
x = find(x);
y = find(y);
if (x == y) return;
if (judge[x] > judge[y])
{
int t = x;
x = y;
y = t;
}
stdnt[x] = y;
judge[y] += judge[x];
}
int main() {
ios::sync_with_stdio(false);
while (cin >> n >> m)
{
if(n==0&&m==0) return 0;
for (int i = 0; i < n; i++) {
judge[i] = 1;
stdnt[i] = i;
}
int num,a,b;
for (int i = 0; i < m; i++) {
cin >> num >> a;
for (int j = 1; j < num; j++) {
cin >> b;
unite(a, b);
}
}
int t = find(0);
cout << judge[t] << endl;
}
}
第三题
掌握魔法的东东
东东在老家农村无聊,想种田。农田有 n 块,编号从 1~n。种田要灌氵
众所周知东东是一个魔法师,他可以消耗一定的 MP 在一块田上施展魔法,使得黄河之水天上来。他也可以消耗一定的 MP 在两块田的渠上建立传送门,使得这块田引用那块有水的田的水。 (1<=n<=3e2)
黄河之水天上来的消耗是 Wi,i 是农田编号 (1<=Wi<=1e5)
建立传送门的消耗是 Pij,i、j 是农田编号 (1<= Pij <=1e5, Pij = Pji, Pii =0)
东东为所有的田灌氵的最小消耗
Input
第1行:一个数n
第2行到第n+1行:数wi
第n+2行到第2n+1行:矩阵即pij矩阵
Output
东东最小消耗的MP值
Example
- input
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0 - output
9
解题思路
这个题需要引入一个超级原点来帮助解决问题,超级原点与其他各点相连,然后再把各个田与所消耗魔法值构成一个图,再用Kruskal求最小生成树即可。
代码实现
#include<iostream>
#include<algorithm>
using namespace std;
struct edge{
int start, end, weight;
edge(){}
edge(int s, int e, int w) { start = s, end = e, weight = w; }
bool operator <(edge a){
return this->weight < a.weight;
}
};
int point[100000];
edge e[100000];
int n,index=0;
edge ini(int s,int e,int w)
{
edge t(s, e, w);
return t;
}
int find(int x)
{
if(point[x]==x) return x;
return point[x] = find(point[x]);
}
int main()
{
cin >> n;
for (int i = 0;i<=n;i++) point[i] = i;
for (int i = 0;i<n;i++){
int w;
cin >> w;
e[index++] = ini(n, i, w);
e[index++] = ini(i, n, w);//n是一个虚构的节点
}
for (int i = 0;i<n;i++)
for (int j = 0;j<n;j++)
{
int t;
cin >> t;
e[index++] = ini(i, j, t);
}
sort(e, e + index);
int ans = 0,count=0;
for (int i = 0;i<index&&count<n;i++)
{
if (find(e[i].start) != find(e[i].end)) {
int E = find(e[i].end), S = find(e[i].start);
point[E] =find(S);
ans += e[i].weight;
count++;
}
}
cout << ans << endl;
}
第四题
数据中心
Example
- input
4
5
1
1 2 3
1 3 4
1 4 5
2 3 8
3 4 2 - output
4
解题思路
这个题与第三题思路很像,利用Kruskal求出最小生成树,并记录最大边权值输出即可。
代码实现
#include<iostream>
#include<algorithm>
using namespace std;
struct edge {
int start, end, weight;
};
edge e[500000];
int judge[500000];
int n, m, r;
int find(int x) {
if (judge[x] == x) return x;
else return judge[x] = find(judge[x]);
}
bool operator < (edge e1, edge e2) {
return e1.weight < e2.weight;
}
int main() {
ios::sync_with_stdio(false);
cin >> n >> m >> r;
for (int i = 0; i < m; i++) cin >> e[i].start >> e[i].end >> e[i].weight;
sort(e, e + m);
for (int i = 1; i <= n; i++) judge[i] = i;
int ans = 0, num = 0;
for (int i = 0; i < m && num != (n - 1); i++)
{
if (find(e[i].start) != find(e[i].end)) {
judge[find(e[i].end)] = find(e[i].start);
ans = max(ans, e[i].weight);
num++;
}
}
cout << ans << endl;
return 0;
}