csp 2019年3月竞赛第三题 有什么高效的算法?

萌新算法水平很弱,这题只得了30分(超时),请大佬们帮优化一下代码
,或者指点萌新一个高效的算法。谢谢


import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner inputer = new  Scanner(System.in);
        //读数据
        int diskCount = inputer.nextInt();//有的硬盘总数
        int stripSize = inputer.nextInt();//一条中有几块
        int avaliableDiskCount = inputer.nextInt();//可用的硬盘数量
        //读取因盘中的信息
        String[] datas = new String[avaliableDiskCount];
        inputer.nextLine();//提前做处理
        for( int x = 0   ; x < avaliableDiskCount ; x++) datas[x] = inputer.nextLine();//读入硬盘的数据
        int outputCount = inputer.nextInt(); //输出的数量
        int[] outputNum = new int[outputCount];
        for(int x = 0 ; x < outputCount ; x ++)outputNum[x] = inputer.nextInt();//输出的序号
        /*
         输入信息检测 ok
        for(int x = 0 ; x < avaliableDiskCount ; x++) System.out.println(datas[x]);//硬盘信息
        for(int x = 0 ; x < outputCount ; x ++)System.out.println(outputNum[x]);//输出的个数
         */


        //创建硬盘阵列
        int pieceCountInOneDisk = (datas[0].length() - (datas[0].indexOf(" ") + 1) ) / 8 ;  //一个硬盘中的最大的分条数
        String[][] diskDatas = new String[pieceCountInOneDisk][diskCount];//阵列数据
        int cutIndex ;//截取硬盘号用的暂时数据计数
        String diskNumInString = "";//用暂存硬盘编号
        int nowDiskNum ; // 当前的硬盘编号
        String everyPieceData = "";//每块中的8位数据
        int nowPieceNum ; //填充硬盘数据是用 的计数器
        boolean[] diskSign = new boolean[diskCount];//硬盘是否可用记号
        for(String s : datas) {
            //截取硬盘号
            cutIndex = 0 ;
            diskNumInString = "";
            while(s.charAt(cutIndex) != ' ') {
                diskNumInString += s.charAt(cutIndex);//拼接数字
                cutIndex ++ ;//结束后是空格所在位置
            }
            nowDiskNum = Integer.parseInt(diskNumInString);//把硬盘编号转换成数值
            diskSign[nowDiskNum] = true ;//该硬盘即可用
            cutIndex ++ ; //移到数据的地方 
            nowPieceNum = 0 ;
            //向该盘中填充数据
            while(true) {//结束条件内部控制
                everyPieceData = "";
                for(int x = 0 ; x < 8 ; x ++ , cutIndex++)everyPieceData += s.charAt(cutIndex);//截取8位
                diskDatas[nowPieceNum][nowDiskNum] = everyPieceData ;//填入对应的盘和对应的块
                if(cutIndex == s.length())break ;
                nowPieceNum ++ ;//结束时候是有效的最大块
            }
        }//for(s) end

        /*
         *测试硬盘阵列的建立 

        for(int x = 0 ; x < pieceCountInOneDisk ; x ++) {
            for(int y = 0 ; y < diskCount ; y ++) {
                if(diskSign[y])System.out.print(diskDatas[x][y] + " ");
                else System.out.print("- ");
            }
            System.out.println();
        }
        */  

        //修复与输出
        int[] nowLocation ;//转换出来的标准位置0:硬盘中的第几块、1:哪个硬盘
        String[] otherData ;
        for(int x = 0 ; x < outputCount ;  x ++) {//遍历输出序列
            nowLocation = location(outputNum[x] , stripSize , diskCount);//定位
            if(diskSign[nowLocation[1]] == true) {//当前的硬盘存在,
                //判断是否超越界限
                if(nowLocation[0] >= pieceCountInOneDisk)System.out.println("-");//输出错误
                else System.out.println(diskDatas[nowLocation[0]][nowLocation[1]]);//输出
            }else if(diskCount - avaliableDiskCount >= 2) {
                //不可修复
                System.out.println("-");
            }else {//可修复进行修复
                otherData = new String[diskCount-1];//其他盘中的数据
                for(int y = 0 , y2 = 0 ; y < diskCount ; y ++) {
                    if(y != nowLocation[1]) {//取得其他恢复用数据
                        otherData[y2] = diskDatas[nowLocation[0]][y];
                        y2++;
                    }
                }//for(y) end
                //进行恢复输出
                System.out.println(fixUp(otherData));
            }

        }   
    }



    public static int[] location(int thePieceNum , int stripSize , int diskCount) {
        int stripLineNum = thePieceNum /( (diskCount - 1) * stripSize);//在第几排条
        int numInThisStriopLine =( thePieceNum %( (diskCount - 1) * stripSize)) / stripSize ;//在当前排条的第几条
        int thisStripLineCickNum = (diskCount - 1) - (stripLineNum % diskCount) ; //当前排条的校验位
        int nowDiskNum = thisStripLineCickNum + 1 == diskCount ? 0 : thisStripLineCickNum + 1 ; //初始化该排第一个数据位
        for(int x = 0 ; x < numInThisStriopLine ; x++)nowDiskNum = nowDiskNum + 1 == diskCount ? 0 :  nowDiskNum + 1 ;//定位到硬盘号
        int thisStripNumInDisk = (stripLineNum * stripSize) + (thePieceNum % stripSize) ;//定位到当前硬盘第几块
        int[] solve =  {thisStripNumInDisk , nowDiskNum};
        return solve ;
    }

    public static String fixUp(String[] otherData) {
        String[][] stemp1 = new String[4][otherData.length];//转成分4位的二进制串

        int index ; //截取起始位置
        String nowTempH ;//截取的16进制串
        String nowTempB ;//街截取的2进制串
        for(int x = 0 ; x < 4 ; x++) {
            for(int y = 0 ; y < otherData.length ; y ++ ) {
                nowTempH = "";
                nowTempB = "";
                index = x * 2 ;//延这个位置截取2个
                nowTempH += otherData[y].charAt(index);//第一个
                index ++ ;
                nowTempH += otherData[y].charAt(index);//第二个
                //转成二进制串
                nowTempB = Integer.toBinaryString(Integer.parseInt(nowTempH , 16));
                //补齐8位
                for(int z = nowTempB.length() ; z < 8 ; z++)nowTempB = "0" + nowTempB ;
                stemp1[x][y] = nowTempB ;//记录
            }
        }//for(x) end

        /*
         * 测试
         * 
        System.out.println("测试stemp1:");
        for(int y = 0 ; y < otherData.length ; y ++ ) {
            for(int x = 0 ; x < 4 ; x ++) {
                System.out.print(stemp1[x][y] + " ");
            }
            System.out.print("         ");
        }
        System.out.println();
        */

        String[] solveInB = new String[4];
        String solve ;//每8位的异或结果
        int oneCount ; // 1 的个数
        for(int x = 0 ; x < 4 ; x ++) {
            solve = "" ;
            for(int z = 0 ; z < 8 ; z ++)  {
                oneCount = 0 ; 
                for(int y = 0 ; y < otherData.length ; y ++){
                    if(stemp1[x][y].charAt(z)=='1')oneCount ++ ;
                }
                solve += oneCount % 2 == 1 ? "1" : "0";
            }
            solveInB[x] = solve ;
        }
        /*
         * 测试

        for(int x = 0 ; x < 4 ; x ++)System.out.print(solveInB[x] + " ");
        System.out.println();
        */

        //最后拼串
        String end = "";
        int endTemp ; //转成10进制 
        String endTempString ;
        for(int x = 0 ; x < 4 ; x ++) {
            endTemp = Integer.parseInt(solveInB[x] , 2);
            endTempString = Integer.toHexString(endTemp);
            for(int y = endTempString.length() ; y < 2 ;  y++)endTempString = "0" + endTempString ;
            end += endTempString.toUpperCase();
        }
        return end ;


    }

}


题主,这个问题我来替你解决,若有帮助,还望采纳,点击回答右侧采纳即可。


  1. 避免使用循环嵌套,尽量减少循环次数,提高代码效率。

  2. 减少数组的使用,可以使用变量代替数组,以减小内存占用。

  3. 在定位硬盘位置时,可以直接使用数学公式计算出来,而不是使用循环来定位。

  4. 在修复数据时,可以避免使用多重循环,直接使用二进制位运算来进行异或操作。

  5. 注意代码的命名规范,尽量使用有意义的变量名、函数名和类名来提高代码的可读性。

下面是一个修改后的代码示例:

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner inputer = new Scanner(System.in);
        //读数据
        int diskCount = inputer.nextInt();//有的硬盘总数
        int stripSize = inputer.nextInt();//一条中有几块
        int avaliableDiskCount = inputer.nextInt();//可用的硬盘数量
        //读取因盘中的信息
        String[] datas = new String[avaliableDiskCount];
        inputer.nextLine();//提前做处理
        for (int x = 0; x < avaliableDiskCount; x++)
            datas[x] = inputer.nextLine();//读入硬盘的数据
        int outputCount = inputer.nextInt(); //输出的数量
        int[] outputNum = new int[outputCount];
        for (int x = 0; x < outputCount; x++)
            outputNum[x] = inputer.nextInt();//输出的序号

        //创建硬盘阵列
        int pieceCountInOneDisk = (datas[0].length() - (datas[0].indexOf(" ") + 1)) / 8;  //一个硬盘中的最大的分条数
        String[][] diskDatas = new String[pieceCountInOneDisk][diskCount];//阵列数据
        boolean[] diskSign = new boolean[diskCount];//硬盘是否可用记号
        for (String s : datas) {
            //截取硬盘号
            int cutIndex = 0;
            String diskNumInString = "";
            while (s.charAt(cutIndex) != ' ') {
                diskNumInString += s.charAt(cutIndex);//拼接数字
                cutIndex++;//结束后是空格所在位置
            }
            int nowDiskNum = Integer.parseInt(diskNumInString);//把硬盘编号转换成数值
            diskSign[nowDiskNum] = true;//该硬盘即可用
            cutIndex++;//移到数据的地方
            int nowPieceNum = 0;
            //向该盘中填充数据
            while (true) {//结束条件内部控制
                String everyPieceData = s.substring(cutIndex, cutIndex + 8);//截取8位
                diskDatas[nowPieceNum][nowDiskNum] = everyPieceData;//填入对应的盘和对应的块
                if (cutIndex + 8 >= s.length()) break;//到数据末尾
                nowPieceNum++;//结束时候是有效的最大块
                cutIndex += 8;
            }
        }//for(s) end

        //定位和修复
        for (int i = 0; i < outputCount; i++) {
            int pieceIndex = outputNum[i] % stripSize;//定位在条内的第几块
            int stripIndex = outputNum[i] / stripSize;//定位在条内的第几个条带
            int checkIndex = (diskCount - 1) - stripIndex % diskCount;//定位奇偶校验所在的硬盘
            boolean flag = true;
            String[] otherData = new String[diskCount - 1];
            for (int j = 0, k = 0; j < diskCount; j++) {
                if (j != checkIndex && diskSign[j]) {//取得其他恢复用数据
                    if (diskDatas[pieceIndex][j] == null) {//数据块不存在
                        flag = false;
                        break;
                    }
                    otherData[k++] = diskDatas[pieceIndex][j];
                }
            }
            if (flag) {//数据可修复
                String repairedData = fixUp(otherData);
                diskDatas[pieceIndex][checkIndex] = repairedData;
                System.out.println(repairedData);
            } else {//不可修复
                System.out.println("-");
            }
        }
    }

    public static String fixUp(String[] otherData) {
        String result = "";
        for (int i = 0; i < 8; i++) {
            int count = 0;
            for (int j = 0; j < otherData.length; j++) {
                if (otherData[j].charAt(i) == '1') {
                    count++;
                }
            }
            result += count % 2 == 0 ? "0" : "1";
        }
        return Integer.toHexString(Integer.parseInt(result, 2)).toUpperCase();
    }
}