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

灾后重建

题目描述
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<Integer> list = new ArrayList<Integer>();
            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);
    }
}
原文地址:https://www.cnblogs.com/a1439775520/p/12947583.html