最短路:最优灌溉(201412-4)
题目描述
问题描述
雷雷承包了很多片麦田,为了灌溉这些麦田,雷雷在第一个麦田挖了一口很深的水井,所有的麦田都从这口井来引水灌溉。
为了灌溉,雷雷需要建立一些水渠,以连接水井和麦田,雷雷也可以利用部分麦田作为“中转站”,利用水渠连接不同的麦田,这样只要一片麦田能被灌溉,则与其连接的麦田也能被灌溉。
现在雷雷知道哪些麦田之间可以建设水渠和建设每个水渠所需要的费用(注意不是所有麦田之间都可以建立水渠)。请问灌溉所有麦田最少需要多少费用来修建水渠。输入格式
输入的第一行包含两个正整数n, m,分别表示麦田的片数和雷雷可以建立的水渠的数量。麦田使用1, 2, 3, ……依次标号。
接下来m行,每行包含三个整数ai, bi, ci,表示第ai片麦田与第bi片麦田之间可以建立一条水渠,所需要的费用为ci。输出格式
输出一行,包含一个整数,表示灌溉所有麦田所需要的最小费用。
样例输入
4 4
1 2 1
2 3 4
2 4 2
3 4 3样例输出
6
样例说明
建立以下三条水渠:麦田1与麦田2、麦田2与麦田4、麦田4与麦田3。
评测用例规模与约定
前20%的评测用例满足:n≤5。
前40%的评测用例满足:n≤20。
前60%的评测用例满足:n≤100。
所有评测用例都满足:1≤n≤1000,1≤m≤100,000,1≤ci≤10,000。
题意解析及思路
学习过数据结构或者图论的话,把题读完应该就能知道,这是求一个最短连通路(即连通所有麦田,且花费最少)
在dijkstra和prim里我这里选择用了prim算法,因为dijkstra这里需要建立一个比较大的邻接图(用vector很有可能要多次扩容使得时间开销增大,直接用数组的话每次遍历这个点可到达的其他点也需要不小的时间开销)
(其实是为了复习一下之前学过的并查集写法,很久没写怕生疏了)
并查集:需要一个数组记录每个点的父节点,需要一个find函数来得到该点父节点
实现prim算法的过程:
1、使用一个n-1次的for循环(最短连通路必定是一个树,那么边数必定是节点数-1,也就是n-1,我们只需要挑选出满足条件的n-1条边即可)
2、对与可以连通的水渠,我们使用结构体来记录端点与其所需要的花费
3、每次循环里,我们只找一个水渠(
为了从小到大不重复遍历,我们在循环外层单独设计一个下标变量loc,用于表示当前已经循环到第几个水渠了),
> 进入while循环(如果该水渠左右两端父节点相同)(遍历水渠的下标++)
> 如果该水渠的左右端点的父节点不同(find(a) != find(b) )那么这个水渠就可以添加到结果res中,同时将b父节点的父节点(f2)更新为a的父节点(f1)(fa[f1] = fa[f2] )
4、循环结束,res中的值即为答案
实现代码(仅供参考)
#include<iostream>
#include<algorithm>
using namespace std;
struct rode{
int n,m;
int weight;
};
bool cmp(const rode&a,const rode&b){
return a.weight<b.weight;
}
rode r[100100]; // 这个结构体数组用于记录所有的水渠信息
int fa[100100];
int find(int child){
if(fa[child] == child)return child;
fa[child] = find(fa[child]);
return fa[child];
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n,m,res = 0,loc = 0; // res 记录总花费 ,loc 记录水渠的下标
cin>>n>>m;
for(int i = 0;i<m;i++)cin>>r[i].m>>r[i].n>>r[i].weight;
for(int i = 1;i<=n;i++)fa[i] = i; //初始化所有点的父节点
sort(r,r+m,cmp); // 将所有水渠的花费从小到大排序
for(int i = 1;i<=n-1;i++){
int a = r[loc].m;
int b = r[loc].n;
while(find(a)==find(b)){
loc++;
a = r[loc].m;
b = r[loc].n;
} // 出了while循环证明这条边一定可取,那么直接进行操作就行
int f1 = find(a);
int f2 = find(b);
fa[f2] = f1;
res += r[loc].weight; // 满足条件即添加到结果中
}
cout<<res;
return 0;
}
森林dfs:网络延时(201503-4)
题目描述
问题描述
给定一个公司的网络,由n台交换机和m台终端电脑组成,交换机与交换机、交换机与电脑之间使用网络连接。交换机按层级设置,编号为1的交换机为根交换机,层级为1。其他的交换机都连接到一台比自己上一层的交换机上,其层级为对应交换机的层级加1。所有的终端电脑都直接连接到交换机上。
当信息在电脑、交换机之间传递时,每一步只能通过自己传递到自己所连接的另一台电脑或交换机。请问,电脑与电脑之间传递消息、或者电脑与交换机之间传递消息、或者交换机与交换机之间传递消息最多需要多少步。输入格式
输入的第一行包含两个整数n, m,分别表示交换机的台数和终端电脑的台数。
第二行包含n - 1个整数,分别表示第2、3、……、n台交换机所连接的比自己上一层的交换机的编号。第i台交换机所连接的上一层的交换机编号一定比自己的编号小。
第三行包含m个整数,分别表示第1、2、……、m台终端电脑所连接的交换机的编号。输出格式
输出一个整数,表示消息传递最多需要的步数。
样例输入
4 2
1 1 3
2 1样例输出
4
样例说明
样例的网络连接模式如下,其中圆圈表示交换机,方框表示电脑:
其中电脑1与交换机4之间的消息传递花费的时间最长,为4个单位时间。样例输入
4 4
1 2 2
3 4 4 4样例输出
4
样例说明
样例的网络连接模式如下:
其中电脑1与电脑4之间的消息传递花费的时间最长,为4个单位时间。评测用例规模与约定
前30%的评测用例满足:n ≤ 5, m ≤ 5。
前50%的评测用例满足:n ≤ 20, m ≤ 20。
前70%的评测用例满足:n ≤ 100, m ≤ 100。
所有评测用例都满足:1 ≤ n ≤ 10000,1 ≤ m ≤ 10000。
题意解析及思路
这题其实就是求森林的两个节点的最大距离。
根据树的常规做法当然是递归,
- 由于是最大距离,我们考虑每个点的时候,如果它有超过一个子节点,那么经过这个点的最长路径一定不会以这个点为终点,而是两个子节点为起始点
- 对于子节点高度的处理,我们对每个子节点都调用dfs获取高度,最终该路径的花费应该是最大子节点的高度(a)+第二大子节点的高度(b)(总共应该是 a + b + 1个点,但实际时间花费其实是这些点连接所需要的边数,也就是a+b)
- 如果子节点只有一个,那么经过该点的最长路就是以该点为终点的路径(根据上面对时间花费的算法,这条路的时间花费就是该子节点的高度)
- 如果没有子节点,那么该点一定只能作为端点,不能起到更新答案res的效果,在dfs中返回当前点的高度1
- 否则dfs中返回的值为deepest+1
实现代码(80分,欢迎大家debug)
#include<iostream>
#include<vector>
using namespace std;
struct node{
vector<node*>child;
int commputer = 0;
node(){}
};
int n,m,res = 0;
node mac[10010];
void build(){
for(int i = 2;i<=n;i++){
int fa ;cin>>fa;
mac[fa].child.push_back(&mac[i]);
}
for(int i = 1;i<=m;i++){
int fa;cin>>fa;
mac[fa].commputer++;
}
}
int dfs(node *root){
if(root == nullptr )return 0;
int deepest = 0,second = 0;
int have = root->commputer;
for(auto it:root->child){
int get = dfs(it);
if(get>deepest)deepest = get;
else if(get>second)second = get;
}
if(deepest == 0 && have){deepest = 1;have--;}
if(second == 0 && have)second = 1;
res = max(res,deepest+second);
return deepest+1;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>n>>m;
build();
node * p = &mac[1];
dfs(p);
cout<<res;
return 0;
}