A-氪金带东
题目:
实验室里原先有一台电脑(编号为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).
Simple Input:
5
1 1
2 1
3 1
1 1
Simple Output:
3
2
3
4
4
题目分析:
最基础的做法就是,对每一个点进行BFS或者DFS,找出每一个点的最大距离的点,这种做法是很消耗时间的,每一次都需要遍历所有的点,那么时间就是n²的,所以为了减少搜索的时间,可以找出数直径的两个端点,在从端点进行DFS或者BFS将最大距离统计出来,时间是O(n);
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
int len[100001];
int m1 = 0;
int l1 ;
struct Node{
int node;
int length;
};
vector<vector<Node> > G;
void dfs(int x,int last,int l)
{
if(l>l1) m1 = x,l1 = l;
if(len[x]<l) len[x] = l;
for(int i = 0;i<G[x].size();i++)
{
if(G[x][i].node == last) continue;
dfs(G[x][i].node,x,G[x][i].length+l);
}
}
int main()
{
int n;
while(cin>>n)
{
G.clear();
G.resize(n+5);
l1 = 0;
int i = 2;
while(i<=n)
{
int a,b;
cin>>a>>b;
Node _node;
_node.node = a;
_node.length = b;
G[i].push_back(_node);
Node _node2;
_node2.node = i;
_node2.length = b;
G[a].push_back(_node2);
i++;
}
memset(len,0,sizeof(len));
dfs(1,-1,0);
dfs(m1,-1,0);
dfs(m1,-1,0);
for(int j =1;j<=n;j++) cout<<len[j]<<endl;
}
// for(int j = 0;j <= n;j++)
// {
// cout<<j<<"->";
// for(vector<Node>::iterator iter = G[j].begin();iter!=G[j].end() ;iter++)
// {
// cout<<iter->node<<" "<<iter->length<<"| ";
// }
// cout<<endl;
// }
return 0;
}
// 0->
//1->2 1| 5 1|
//2->1 1| 3 1|
//3->2 1| 4 1|
//4->3 1|
//5->1 1|
B-戴好口罩
题目:
新型冠状病毒肺炎(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:
输出要隔离的人数,每组数据的答案输出占一行
Sample 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
Sample Output:
4
1
1
题目分析:
并查集的板子题,为提高查询速度可以将所有节点挂在一个节点之下
记得在每次用完之后要清空vector
代码如下:
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 3e4+5;
int par[maxn];
int r[maxn];
void init(int n)
{
for(int i = 0;i<=n;i++)
{
par[i] = i;r[i] = 1;
}
}
int find(int x)
{
return par[x]==x ? x : par[x]=find(par[x]);
}
bool unit(int x,int y)
{
x = find(x);y = find(y);
if(x==y) return false;
par[x] = y;
r[y]+=r[x];
return true;
}
int main()
{
int m,n;
while(~scanf("%d%d",&n,&m))
{
if(m==0&&n==0) break;
init(n);
while(m--)
{
int num;
scanf("%d",&num);
int last = -1;
while(num--)
{
int a;
scanf("%d",&a);
if(last!=-1) unit(a,last);
last = a;
}
}
cout<<r[find(0)]<<endl;
}
}
C-农田问题
题目;
东东在老家农村无聊,想种田。农田有 n 块,编号从 1~n。种田要灌氵
众所周知东东是一个魔法师,他可以消耗一定的 MP 在一块田上施展魔法,使得黄河之水天上来。他也可以消耗一定的 MP 在两块田的渠上建立传送门,使得这块田引用那块有水的田的水。 (1<=n<=3e2)
黄河之水天上来的消耗是 Wi,i 是农田编号 (1<=Wi<=1e5)
建立传送门的消耗是 Pij,i、j 是农田编号 (1<= Pij <=1e5, Pij = Pji, Pii =0)
东东为所有的田灌氵的最小消耗
IMPUT;
第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
题目分析;
最小生成树版子题,可以形成最小生成树之后,再加上一个消耗最小的农田灌溉,或者将生成一个超级原点,到所有的农田的距离为到灌溉的距离,直接套用最小生成树
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 3e2+5;
using namespace std;
int par[maxn];
int tot;
int n;
void init(int n){for(int i = 0;i<=n;i++) par[i] = i;}
int find(int x){return par[x]==x? x:par[x] = find(par[x]);}
struct Edge{
int u,v,len;
bool operator<(const Edge& b)
{
return len < b.len;
}
}Es[maxn*maxn];
int krukal()
{
init(n);
sort(Es,Es+tot);
int cnt = 0,ans = 0;
int x,y;
for(int i = 0;i<tot&&cnt<=n;i++)
{
x = find(Es[i].u),y=find(Es[i].v);
if(x!=y)
{
ans+=Es[i].len;
cnt++;
par[x] = y;
}
}
return ans;
}
int main()
{
cin>>n;
tot=0;
for(int i =1;i<=n;i++) cin>>Es[tot].len,Es[tot].u=0,Es[tot].v=i,tot++;
for(int i = 1;i<=n;i++)
{
for(int j = 1;j<=n;j++)
{
cin>>Es[tot].len;
if(j<i)
{
Es[tot].u = i;
Es[tot].v=j;
tot++;
}
}
}
cout<<krukal();
}
D-数据中心
Sample Input:
4
5
1
1 2 3
1 3 4
1 4 5
2 3 8
3 4 2
Sample Output;
4
题目分析:
此题只是题目较长,在观察样例之后,发现人root这个点没有多少用处,就是一个最小生成树问题
代码如下:
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;
const int maxn =1e5;
int par[maxn];
int cnt = 1;
void init()
{
for(int i = 0;i<maxn;i++) par[i] = i;
}
int find(int x)
{
return par[x]==x? x:par[x] = find(par[x]);
}
//bool unit(int x,int y)
//{
// x = find(x),y = find(y);
// if(x==y) return false;
// par[x] = y;
// return true;
//}
struct Edge{
int u,v,len;
Edge(int f,int t,int l):u(f),v(t),len(l){
}
Edge()
{
}
bool operator<(const Edge& b)
{
return len < b.len;
}
};
Edge edge[100010];
int main()
{
int m,n ,root;
cin>>n>>m>>root;
for(int i = 1,u,v,t;i<=m;i++)
{
cin>>u>>v>>t;
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt++].len = t;
}
sort(edge+1,edge+m+1);
init();
int ans = -1;
for(int i = 1,temp = 0;temp!=n-1;i++)
{
int x = find(edge[i].u);int y = find(edge[i].v);
if(x!=y)
{
par[x] = y;
temp++;
ans = edge[i].len;
}
}
cout<<ans;
}