主页

题目要求:
kotor拿到了一些正整数。 她决定从每个正整数取出一个素因子。但是, kotori有强迫症, 她不允许两个不同的正整数取出相同的素因子。她想知道,最终所有取出的数的和的最小值是多少?
注:若a mod k== 0,则称k是a的因子。若一个数有且仅有两个因子,则称其是素数。显然1只有一个因子,不是素数。

输入:12 15 28 22
输出:17

分析:
数字12分解得到质因数:2 2 3, 有效质因数:2 3
数字15分解得到质因数:3 5 有效质因数:3 5
数字28分解得到质因数:2 2 7 有效质因数:2 7
数字12分解得到质因数:2 11 有效质因数:2 11

转换成矩阵得到(第一列是质因数升序排列,第一排是输入的数字):

.    12    15    22    28

2    1    0    1    1

3    1    1    0    0

5    0    1    0    0

7    0    0    0    1

11    0    0    1    0

从上矩阵发现规律,列数为4,行数为5,而案例给的17正好等于2+3+5+7。
即答案规则类似于如下:
行数>=列数 取前(列数)行的质因数相加即可。
行数<列数 则无法分配质因数,提示“错误”。

另外假设现给 12 24 13,则得到以下矩阵

     12    24    13
2    1    1    0
3    1    1    0
13    0    0    1

验证通过

但是如果再变一下用户输入的数据 12 13,则得到以下矩阵

     12    13
2    1    0
3    1    0
13    0    1

此时发现验证不通过,根据规则,取前两行质因数,程序输出得到5,但是实际需要2+13=15。
所以根据此时分析得到,我们在生成质因数矩阵表的时候需要排除用户输入的质因数数字,单独把该质因数数字最后拿来相加,即优化后得到的矩阵表为:

    12
2    1
3    1

得到质因数2,再加上用户输入的质因数13,即得到15.

下面是代码,proc2是简易版【易懂的】,【proc3】是同原理优化版,纯算法。

import java.util.*;

public class Test06 {
    static boolean DEBUG=false;

    public static Integer proc2(int[] nums){
        //int[][] map=new int[1000][nums.length];
        List<Integer> keyList=new LinkedList();//质数列
        boolean[] ket_exists=new boolean[1000];
        boolean[] n2=new boolean[1000];//获得质数表

        int c=0;
        //通过取反来获得质数表
        for (int i = 2; i < 500; i++)
            for (int j = 2; j < (1000/i)+1; j++) {
                int k1=i*j;
                if(k1>=1000)break;
                n2[k1]=true;
                c++;
            }
        for (int i=2;i<1000;i++)if(!n2[i])keyList.add(i);
        List<Integer> kTable=new LinkedList();//合数表
        List<Integer> zTable=new LinkedList();//输入数字的质数表
        // 分离质数合数,因为质数只有1和他本身,它本身就是质数,就没有比较加入后续的计算。
        for (int i = 0; i < nums.length; i++) {
            int num_t=nums[i];
            if (keyList.contains(num_t)){
                zTable.add(num_t);        //加入质数表
                nums[i]=0;
            }else{
                kTable.add(num_t);        //加入合数表
            }
        }



        //重置一下用户输入的数列,默认后面都只处理合数的数列
        if(kTable.size()>0){
            Arrays.sort(nums);      //排个序。
            nums=Arrays.copyOfRange(nums,zTable.size(),nums.length);//清除上面标记为0得记录
        }else{
            nums=new int[0];   //用户全给的质数数列,没有合数。。。
        }

        int max_offset_h=0;         //最大因数质数的大小,后面会有用。
        //放到表里
        for (int num : nums) {
            int tn = num;                                   //取出数字,保证不会影响序列里面的。
            for (int x : keyList) {
                if (tn == 1) break;                         //只剩1了,跳出循环
                if (tn % x != 0) continue;                  //无法整除,不是因数,跳到下一循环
                if (x > tn) break;                          //除数比被除数大了,跳出循环,很有概率程序出现问题了【不在范围内】
                if (!ket_exists[x]) ket_exists[x] = true;   //当该质数因数没有用过,就标记一下,该质数入库了。
                if (max_offset_h < x) max_offset_h = x;     //标记一下使用最大的因数数字是多少
                do {
                    tn = tn / x;                            //做除法
                } while (tn % x == 0);                      //如果下一阶段还能除,则继续除。
            }
        }

        //如果二维表的行数>列数,则只取前[列数]个行的质因数。
        //如果二维表的行数=列数,直接加完就行。
        //如果二维表的行数<列数,那么质因数个数肯定不够用,则报异常。


        int table_height_max=0;                             //定义当前记录的行数【也就是因数个数】
        int sum_k=0;                                        //定义结果记录值
        for(int k:keyList){                                 //循环一下质数队列
            if(ket_exists[k])table_height_max+=1;           //如果
            if(k>max_offset_h) break;                       //如果超出上面记录的最大的质因数,就跳出
            if(table_height_max<=nums.length)sum_k+=k;      //如果当前行的行号小于或等于列数,则把值加入。
        }


        if(table_height_max<nums.length){                   //如果表的总行数小于合数的个数,就异常。
            if(DEBUG)System.out.println("proc2无法计算");
            return null;
        }
        if(zTable.size()>0) for (int n:zTable) {
            sum_k+=n;
        }
        if(DEBUG)System.out.println("proc2值为:"+sum_k);
        return sum_k;
    }

    public static Integer proc3(int[] nums){
        Set<Integer> ket_exists=new TreeSet();
        List<Integer> zTable=new LinkedList();//输入数字的质数表
        int max_offset_h=0;         //最大因数质数的大小,后面会有用。
        //分析数据,并放到表里。
        for (int num : nums) {
            int tn = num;                                   //取出数字,保证不会影响序列里面的。
            for (int x = 2; x <= Math.sqrt(tn); x++) {
                if (tn == 1) break;                         //只剩1了,跳出循环
                if (tn % x != 0) continue;                  //无法整除,不是因数,跳到下一循环
                if (x > tn) break;                          //除数比被除数大了,跳出循环,很有概率程序出现问题了【不在范围内】
                ket_exists.add(x);                          //该质数入库了。
                if (max_offset_h < x) max_offset_h = x;     //标记一下使用最大的因数数字是多少
                do {
                    tn = tn / x;                            //做除法
                } while (tn % x == 0);                      //如果下一阶段还能除,则继续除。
            }
            if(tn==num) {
                zTable.add(num);
                continue;}         // 分离质数合数,因为质数只有1和他本身,它本身就是质数,就没有比较加入后续的计算。
            if(tn==1)continue;
            ket_exists.add(tn);
        }

        int sum_k=0,i=0;
        for (int k:ket_exists)if(i<nums.length-zTable.size()){ sum_k+=k;i++;}   //最终的统计结果。

        if(ket_exists.size()<nums.length-zTable.size()){                   //如果表的总行数【质因数】小于合数【用户输入的数字组的合数】的个数,就异常。
            if(DEBUG)System.out.println("proc3:无法计算。");
            return null;
        }
        if(zTable.size()>0) for (int n:zTable) sum_k+=n;                    // 用户输入的质数数字直接加入就行。
        if(DEBUG)System.out.println("proc3值为:"+sum_k);                     //输出
        return sum_k;
    }


    public static void main(String[] args) {
        int[][] testData= new int[][]{
                new int[]{12,15,28,22},
                new int[]{12,15,25,13},
                new int[]{12,15,28,13},
                new int[]{10,15,28,8,20},
        };
        Integer[] result=new Integer[]{17,23,23,null};
        System.out.println("基础正确性测试");
        for (int i = 0; i < testData.length; i++) {
            if(Objects.equals(proc2(Arrays.copyOf(testData[i],testData[i].length)), result[i])){ System.out.println("proc2测试通过"); }
            if(Objects.equals(proc3(Arrays.copyOf(testData[i],testData[i].length)), result[i])){ System.out.println("proc3测试通过"); }
        }

        int count=10000;
        long time=System.currentTimeMillis();

        for (int j = 0; j < count; j++) {
            for (int[] testDatum : testData) {
                proc2(Arrays.copyOf(testDatum, testDatum.length));
            }
        }
        long time2=System.currentTimeMillis();
        System.out.println("proc2 "+count+"次测试:"+(time2-time)+"ms");


        for (int j = 0; j < count; j++) {
            for (int[] testDatum : testData) {
                proc3(Arrays.copyOf(testDatum, testDatum.length));
            }
        }
        long time3=System.currentTimeMillis();
        System.out.println("proc3 "+count+"次测试:"+(time3-time2)+"ms");


        count=1000000;
        for (int j = 0; j < count; j++) {
            for (int[] testDatum : testData) {
                proc3(Arrays.copyOf(testDatum, testDatum.length));
            }
        }

        long time4=System.currentTimeMillis();
        System.out.println("proc3 "+count+"次测试:"+(time4-time3)+"ms");

    }
    public static void input(){
        Scanner sc=new Scanner(System.in);
        System.out.println("等待输入");
        int count=sc.nextInt();
        System.out.println("个数:"+count);
        int[] nums=new int[count];
        for (int i = 0; i < count; i++) nums[i]=sc.nextInt();
        proc3(nums);
    }
}

版权属于:WANYL
作品采用:本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
0

目录

来自 《Java赛题 kotori和素因子【不重复的质因数相加求最小】》
评论

WANYL

博主很懒,啥都没有
123 文章数
0 评论量
11 分类数
124 页面数
已在风雨中度过 3年289天14小时5分