蓝桥杯灾后重建java,java实现第六届蓝桥杯灾后重建

本文介绍了一种使用图论算法解决灾后重建道路修复问题的方法。通过Prim算法构建最小生成树,再利用Floyd算法找出最短路径,最后在限定条件下寻找最大权值边,以确定最小时间成本的修复方案。样例展示了如何处理具体的询问并求解所需最少时间。
摘要由CSDN通过智能技术生成

灾后重建

题目描述

Pear市一共有N(<=50000)个居民点,居民点之间有M(<=200000)条双向道路相连。这些居民点两两之间都可以通过双向道路到达。这种情况一直持续到最近,一次严重的地震毁坏了全部M条道路。

震后,Pear打算修复其中一些道路,修理第i条道路需要Pi的时间。不过,Pear并不打算让全部的点连通,而是选择一些标号特殊的点让他们连通。

Pear有Q(<=50000)次询问,每次询问,他会选择所有编号在[l,r]之间,并且 编号 mod K = C 的点,修理一些路使得它们连通。由于所有道路的修理可以同时开工,所以完成修理的时间取决于花费时间最长的一条路,即涉及到的道路中Pi的最大值。

你能帮助Pear计算出每次询问时需要花费的最少时间么?这里询问是独立的,也就是上一个询问里的修理计划并没有付诸行动。

【输入格式】

第一行三个正整数N、M、Q,含义如题面所述。

接下来M行,每行三个正整数Xi、Yi、Pi,表示一条连接Xi和Yi的双向道路,修复需要Pi的时间。可能有自环,可能有重边。1<=Pi<=1000000。

接下来Q行,每行四个正整数Li、Ri、Ki、Ci,表示这次询问的点是[Li,Ri]区间中所有编号Mod Ki=Ci的点。保证参与询问的点至少有两个。

【输出格式】

输出Q行,每行一个正整数表示对应询问的答案。

【样例输入】

7 10 4

1 3 10

2 6 9

4 1 5

3 7 4

3 6 9

1 5 8

2 7 4

3 2 10

1 7 6

7 6 9

1 7 1 0

1 7 3 1

2 5 1 0

3 7 2 1

【样例输出】

9

6

8

8

【数据范围】

对于20%的数据,N,M,Q<=30

对于40%的数据,N,M,Q<=2000

对于100%的数据,N<=50000,M<=2*10^5,Q<=50000. Pi<=10^6. Li,Ri,Ki均在[1,N]范围内,Ci在[0,对应询问的Ki)范围内。

资源约定:

峰值内存消耗(含虚拟机) < 256M

CPU消耗 < 5000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。

注意:主类的名字必须是:Main,否则按无效代码处理。

import java.util.ArrayList;

import java.util.Scanner;

public class Main {

//使用Prim算法,获取输入图的最小生成树

public int[][] getPrim(int[][] value) {

int[][] result = new int[value.length][value[0].length]; //存放最终最小生成树的边权值

int[] used = new int[value.length]; //用于判断顶点是否被遍历

for(int i = 1, len = value.length;i < len;i++)

used[i] = -1; //初始化,所有顶点均未被遍历

used[1] = 1; //从顶点1开始遍历,表示顶点已经被遍历

int count = 1; //记录已经完成构造最小生成树的顶点

int len = value.length;

while(count < len) { //当已经遍历的顶点个数达到图的顶点个数len时,退出循环

int tempMax = Integer.MAX_VALUE;

int tempi = 0;

int tempj = 0;

for(int i = 1;i < len;i++) { //用于遍历已经构造的顶点

if(used[i] == -1)

continue;

for(int j = 1;j < len;j++) { //用于遍历未构造的顶点

if(used[j] == -1) {

if(value[i][j] != 0 && tempMax > value[i][j]) {

tempMax = value[i][j];

tempi = i;

tempj = j;

}

}

}

}

result[tempi][tempj] = tempMax;

result[tempj][tempi] = tempMax;

used[tempj] = 1;

count++;

}

return result;

}

//使用floyd算法获取所有顶点之间的最短路径的具体路径

public void floyd(int[][] primTree, int[][] path) {

int[][] tree = new int[primTree.length][primTree.length];

for(int i = 1;i < primTree.length;i++)

for(int j = 1;j < primTree.length;j++)

tree[i][j] = primTree[i][j];

for(int k = 1;k < primTree.length;k++) {

for(int i = 1;i < primTree.length;i++) {

for(int j = 1;j < primTree[0].length;j++) {

if(tree[i][k] != 0 && tree[k][j] != 0) {

int temp = tree[i][k] + tree[k][j];

if(tree[i][j] == 0) {

tree[i][j] = temp;

path[i][j] = k; //存放顶点i到顶点j之间的路径节点

}

}

}

}

}

}

//返回a与b之间的最大值

public int max(int a, int b) {

return a > b ? a : b;

}

//根据最短路径,返回顶点start~end之间的最大权值边

public int dfsMax(int[][] primTree, int[][] path, int start, int end) {

if(path[start][end] == 0)

return primTree[start][end];

int mid = path[start][end]; //start和end的中间顶点

return max(dfsMax(primTree, path, start, mid), dfsMax(primTree, path, mid, end));

}

//根据最小生成树,返回各个顶点到其它顶点行走过程中,权值最大的一条边

public int[][] getMaxValue(int[][] primTree) {

int[][] path = new int[primTree.length][primTree[0].length];

floyd(primTree, path); //获取具体最短路径

int[][] result = new int[primTree.length][primTree[0].length];

for(int i = 1;i < primTree.length;i++) {

for(int j = 1;j < primTree.length;j++) {

if(j == i)

continue;

int max = dfsMax(primTree, path, i, j);

result[i][j] = max;

}

}

return result;

}

//打印出题意结果

public void printResult(int[][] value, int[][] result) {

int[][] primTree = getPrim(value); //获取输入图的最小生成树

int[][] maxResult = getMaxValue(primTree); //获取各个顶点到其它顶点最短路径中最大权值边

for(int i = 0;i < result.length;i++) {

int L = result[i][0];

int R = result[i][1];

int K = result[i][2];

int C = result[i][3];

ArrayList list = new ArrayList();

for(int j = L;j <= R;j++) {

if(j % K == C)

list.add(j);

}

int max = 0;

for(int j = 0;j < list.size();j++) {

for(int k = j + 1;k < list.size();k++) {

if(max < maxResult[list.get(j)][list.get(k)])

max = maxResult[list.get(j)][list.get(k)];

}

}

System.out.println(max);

}

return;

}

public static void main(String[] args) {

Main test = new Main();

Scanner in = new Scanner(System.in);

int N = in.nextInt();

int M = in.nextInt();

int Q = in.nextInt();

int[][] value = new int[N + 1][N + 1];

for(int i = 1;i <= M;i++) {

int a = in.nextInt();

int b = in.nextInt();

int tempV = in.nextInt();

value[a][b] = tempV;

value[b][a] = tempV;

}

int[][] result = new int[Q][4];

for(int i = 0;i < Q;i++) {

result[i][0] = in.nextInt();

result[i][1] = in.nextInt();

result[i][2] = in.nextInt();

result[i][3] = in.nextInt();

}

test.printResult(value, result);

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值