算法笔记_067:蓝桥杯练习 算法训练 安慰奶牛(Java)
目录
1 问题描述
Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路。道路被用来连接N个牧场,牧场被连续地编号为1到N。每一个牧场都是一个奶牛的家。FJ计划除去P条道路中尽可能多的道路,但是还要保持牧场之间 的连通性。你首先要决定那些道路是需要保留的N-1条道路。第j条双向道路连接了牧场Sj和Ej(1 <= Sj <= N; 1 <= Ej <= N; Sj != Ej),而且走完它需要Lj的时间。没有两个牧场是被一条以上的道路所连接。奶牛们非常伤心,因为她们的交通系统被削减了。你需要到每一个奶牛的住处去安慰她们。每次你到达第i个牧场的时候(即使你已经到过),你必须花去Ci的时间和奶牛交谈。你每个晚上都会在同一个牧场(这是供你选择的)过夜,直到奶牛们都从悲伤中缓过神来。在早上 起来和晚上回去睡觉的时候,你都需要和在你睡觉的牧场的奶牛交谈一次。这样你才能完成你的 交谈任务。假设Farmer John采纳了你的建议,请计算出使所有奶牛都被安慰的最少时间。
第1行包含两个整数N和P。
接下来N行,每行包含一个整数Ci。
接下来P行,每行包含三个整数Sj, Ej和Lj。
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6
5 <= N <= 10000,N-1 <= P <= 100000,0 <= Lj <= 1000,1 <= Ci <= 1,000。
2 解决方案
首先说一下这个题目的问题以及题意的理解:
问题:
题目给出的输入样例数据,其中顶点数为5,边数为7,可是样例数据中的实际只有6条边的数据,这个有点坑啊...,后来提交代码,看了一下第一组测试数据,这组数据就是样例数据,其第7条边数据为 4 5 7。
期题意中样例输入输出数据应为:
样例输入
5 7
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6
4 5 12
样例输出
176
题意理解:初步读这题,为了理解题意也是读了三四遍,其中:你每个晚上都会在同一个牧场(这是供你选择的)过夜,直到奶牛们都从悲伤中缓过神来。这句话我也是醉了,为了描述一个题目,也不要弄得这么文艺呀。该题传到意思核心:即首先找到n个牧场的最小生成树,然后,选择其中一个开始进行遍历,最后要回到起点点牧场。
分析到这里,大家要注意,这里有一个小坑,那就是怎么寻找最小生成树?即是直接使用牧场之间的距离行走时间Lj来确定最小生成树,还是要把每个牧场交谈时间Ci也算入最小生成树的判断?
我刚开始做的时候,是使用Li来确定最小生成树,然后选择其中Ci最小的牧场开始出发,最后回到出发点,会发现对于题目中所给的样例输入数据测试,其输出为178,而不是题目中的176。
然后,我经过检查发现2 4 12,4 5 12这两条边选择不同其最终的输出样例也不同,顶点2的Ci为10,顶点5的Ci为30,然后自己测试了一次,如果最小生成树选择4 5 12,样例输出为178,如果选择2 4 12样例输出为176。这里面影响的原因就是构造的最小生成树不对。
所以,到了这里我们再分析一下,从顶点1到顶点2所耗费时间为2*L12 + C1 + C2,这里为什么会有2 * L12呢?因为题意中说明从某一个牧场出发后,最终还要返回这个牧场,说明每一条边要行走2次,即一去一回。那么又有新的问题了,为什么C1和C2不乘以2呢?我们在看看最小生成树,就会发现,如果一个牧场,只有一条边连接,其顶点值计算一次,如果有2条边,就计算2次,3条边就计算3次,依次类推。那么在公式里2 * L12 + C1 + C2就很好的说明了一点,那就是一个牧场有几条边,那它的顶点就被加了几次。
这里就可以理解为顶点C1到C2的边的权值为 2*L12 + C1 + C2,然后,利用这个权值求取最小生成树。
到了这里问题已经解决了百分之九十五了,最后,再选择一个牧场顶点Ci最小的值,用这么最小Ci加上最小生成树的权值和就可以得到最终的结果啦。
此处还有一点问题就是,下面我的代码在练习系统中的最终评分为70分,
具体原因为:运行超时。

然后,我用同样方法的C语言代码在系统里运行后,测试结果为100分(PS:此处代码可以看出文末给出的参考资料)。可以看出Java和C/C++运行时间有很大差异啊。(PS:如有同学路过,发现不是语言运行性能的原因,而是选用方法不当造成运行超时的话,希望不吝赐教呀~)


下面代码中,我是使用Kruskal算法实现,具体原理可以参考楼主的另一篇博客:算法笔记_066:Kruskal算法详解(Java)
具体代码如下:
import java.util.Scanner;
public class Main {
class edge {
public int a;
public int b;
public int value;
edge(int a, int b, int value) {
this.a = a;
this.b = b;
this.value = value;
}
}
public void edgeSort(edge[] A){
if(A.length > 1) {
edge[] leftA = getHalfEdge(A, 0);
edge[] rightA = getHalfEdge(A, 1);
edgeSort(leftA);
edgeSort(rightA);
mergeEdgeArray(A, leftA, rightA);
}
}
public edge[] getHalfEdge(edge[] A, int judge) {
edge[] half;
if(judge == 0) {
half = new edge[A.length / 2];
for(int i = 0;i < A.length / 2;i++)
half[i] = A[i];
} else {
half = new edge[A.length - A.length / 2];
for(int i = 0;i < A.length - A.length / 2;i++)
half[i] = A[A.length / 2 + i];
}
return half;
}
public void mergeEdgeArray(edge[] A, edge[] leftA, edge[] rightA) {
int i = 0;
int j = 0;
int len = 0;
while(i < leftA.length && j < rightA.length) {
if(leftA[i].value < rightA[j].value) {
A[len++] = leftA[i++];
} else {
A[len++] = rightA[j++];
}
}
while(i < leftA.length) A[len++] = leftA[i++];
while(j < rightA.length) A[len++] = rightA[j++];
}
//获取节点a的根节点
public int find(int[] id, int a) {
int x, r, k;
r = a;
while(id[r] >= 0) r = id[r];
k = a;
while(k != r) {
x = id[k];
id[k] = r;
k = x;
}
return r;
}
//合并a节点所在树和b节点所在树
public void union(int[] id, int a, int b) {
int ida = find(id, a);
int idb = find(id, b);
int num = id[ida] + id[idb];
if(id[ida] < id[idb]) {
id[idb] = ida;
id[ida] = num;
} else {
id[ida] = idb;
id[idb] = num;
}
}
//获取题意最终结果
public void getMinSpanTree(edge[] A, int[] valueN) {
int sum = 0;
int[] id = new int[valueN.length];
for(int i = 0;i < valueN.length;i++)
id[i] = -1;
edgeSort(A);
int count = 0;
for(int i = 0;i < A.length;i++) {
int a = A[i].a;
int b = A[i].b;
int ida = find(id, a - 1);
int idb = find(id, b - 1);
if(ida != idb) {
sum += A[i].value;
count++;
union(id, a - 1, b - 1);
}
if(count >= valueN.length - 1)
break;
}
int minValueN = valueN[0];
for(int i = 0;i < valueN.length;i++) {
if(minValueN > valueN[i]) {
minValueN = valueN[i];
}
}
sum += minValueN;
System.out.println(sum);
}
public static void main(String[] args){
Main test = new Main();
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int p = in.nextInt();
if(n > 10000 || n < 5)
return;
if(p > 100000 || p < n - 1)
return;
int[] valueN = new int[n];
for(int i = 0;i < n;i++)
valueN[i] = in.nextInt();
edge[] A = new edge[p];
for(int i = 0;i < p;i++) {
int a = in.nextInt();
int b = in.nextInt();
int value = in.nextInt() * 2 + valueN[a - 1] + valueN[b - 1];
A[i] = test.new edge(a, b, value);
}
test.getMinSpanTree(A, valueN);
}
}
参考资料:
算法笔记_067:蓝桥杯练习 算法训练 安慰奶牛(Java)的更多相关文章
- 算法笔记_107:蓝桥杯练习 算法提高 学霸的迷宫(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗.但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要 ...
- 算法笔记_096:蓝桥杯练习 算法提高 求最大值(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 给n个有序整数对ai bi,你需要选择一些整数对 使得所有你选定的数的ai+bi的和最大.并且要求你选定的数对的ai之和非负,bi之和非负 ...
- 算法笔记_081:蓝桥杯练习 算法提高 矩阵乘法(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 有n个矩阵,大小分别为a0*a1, a1*a2, a2*a3, ..., a[n-1]*a[n],现要将它们依次相乘,只能使用结合率,求最 ...
- 算法笔记_077:蓝桥杯练习 K好数(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 如果一个自然数N的K进制表示中任意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数.求L位K进制数中K好数的数目.例如K = 4, ...
- 算法笔记_106:蓝桥杯练习 算法提高 周期字串(Java)
目录 1 问题描述 2 解决方案 2.1 第一印象解法(80分) 2.2 借鉴网友解法(100分) 1 问题描述 问题描述 右右喜欢听故事,但是右右的妈妈总是讲一些“从前有座山,山里有座庙,庙里有 ...
- 算法笔记_102:蓝桥杯练习 算法提高 快乐司机(Java)
目录 1问题描述 2 解决方案 1 问题描述 问题描述 "嘟嘟嘟嘟嘟嘟 喇叭响 我是汽车小司机 我是小司机 我为祖国运输忙 运输忙" 这是儿歌“快乐的小司机”.话说现在当司机光 ...
- 算法笔记_099:蓝桥杯练习 算法提高 排列数(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 0.1.2三个数字的全排列有六种,按照字母序排列如下: 012.021.102.120.201.210 输入一个数n 求0~9十个数的全排 ...
- 算法笔记_061:蓝桥杯练习 字串统计(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 给定一个长度为n的字符串S,还有一个数字L,统计长度大于等于L的出现次数最多的子串(不同的出现可以相交),如果有多个,输出最长的,如果仍然 ...
- 算法笔记_123:蓝桥杯第七届省赛(Java语言B组部分习题)试题解答
目录 1 凑算式 2 方格填数 3 四平方和 1 凑算式 凑算式 B DEF A + --- + ------- = 10 C GHI (如果显示有问题,可以参见[图1.jpg]) 这个算式中A ...
随机推荐
- 理解 tornado.gen
转自:http://blog.xiaogaozi.org/2012/09/21/understanding-tornado-dot-gen/ 理解 tornado.gen SEP 21ST, 2012 ...
- TCP阻塞模式开发
在阻塞模式下,在IO操作完成前,执行的操作函数将一直等候而不会立刻返回,该函数所在的进程会阻塞在这里.相反,在非阻塞模式下,套接字函数会立即返回,而不管IO是否完成,该函数所在的线程将继续运行.阻塞模 ...
- 报表系统OLAP
实现报表系统可自定义定制: 1.纬度,事实表,用户自定义观察纬度,实现报表自定义(自定义纬度树结构---通过这个树结构,后台自动生成报表SQL,用户仅仅配置纬度关系即可)----报表配置纬度关 系 2 ...
- CSS Web安全字体组合
常用的字体组合 font-family属性是多种字体的名称,作为一个"应变"制度,以确保浏览器/操作系统之间的最大兼容性.如果浏览器不支持的第一个字体,它尝试下一个的字体. 你想要 ...
- python的XML处理模块ElementTree
ElementTree是python的XML处理模块,它提供了一个轻量级的对象模型.它在Python2.5以后成为Python标准库的一部分,但是Python2.4之前需要单独安装.在使用Elemen ...
- linux优化之全过程
基于开放源代码的Linux给用户提供了这样一个平台:可以根据自己的软.硬件环境,定制自己的Linux应用环境.因此,根据每个用户不同的应用范围定制应用环境,可以将Linux系统的性能提升到新的高度. ...
- Python自定义状态码枚举类
在Java里很容易做到自定义有状态码和状态说明的枚举类例如: public enum MyStatus { NOT_FOUND(404, "Required resource is not ...
- uva-185-暴力枚举
请相信,这是一道水题,读了一周的题意 题意: 题目里面描述的那三个条件可以直接无视,关于罗马数字只要知道一个规则即可,映射如下 I 1 V 5X 10 L 50C 100 D 50 ...
- 4.struts2的配置文件优先级
转自:https://wenku.baidu.com/view/84fa86ae360cba1aa911da02.html 在struts2中一些配置(比如常量)可以同时在struts-default ...
- border做三角符号
用border做三角符号以及其他图形 ;; border-width:20px 10px; border-style:solid; border-color:#ff3300 #ff3300 #ffff ...