和的逆运算问题 | 豆包MarsCode AI刷题

138 阅读4分钟

思路: 对于无序得数组我们先对他进行从大到小排序,使他变得有序。于是我们就得到一个递增得序列,假设我们要求的做加法运算的数也是有序的那么 sums排列后对应的两两相加的数在我们要求的解里面是 : 1+2 、1+3、1+4、1+5…… 2+3、2+4……3+4……(里面的数字代表答案数字的序号)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class 和的逆运算问题 {
    public static String solution(int n, int[] sums) {

        if (n<3){
            return "Impossible";
        }
        //先对sums进行排列
        Arrays.sort(sums);
        System.out.println("排列后的和数组:"+Arrays.toString(sums));
        //先得到第2个数和第3个数的关系
        int sub32=sums[1]-sums[0];
        //算出第二个数
        int two=(sums[n-1]-sub32)/2;
        //算出第一个数
        int one= sums[0]-two;
        System.out.println("解得第一个数:"+one);
        //接下来得到所有的数
        int [] nums=new int[n];
        nums[0]=one;
        nums[1]=two;
        //从第三个数开始
        int res=2;
        for(int i=1;res<n;i++){
            //TODO:这里用while过不了,需要用if!!不能确定重复的解有多少个,如果题目的重复解出现两个以上就不能通过
            if(i<sums.length&&sums[i]==sums[i-1]){
                i++;
            }
            if(i== sums.length){
                return "Impossible";
            }
            nums[res]=sums[i]-one;
            res++;
        }

        System.out.println("解得加数数组:"+Arrays.toString(nums));
        //接下来验证是否合法
        List<Integer> verilist=new ArrayList<>();
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                verilist.add(nums[i]+nums[j]);
            }
        }

        //也需要对验证数组配列,确保顺序,防止出现2+3,1+4都是5但是位置不同的情况
        verilist.sort(null);
        System.out.println("验证数组verilist:"+verilist);

        for(int i=0;i<sums.length;i++){
            if(sums[i]!=verilist.get(i)){
                return "Impossible";
            }
        }

        StringBuilder sb=new StringBuilder();
        for (int i=0;i<n;i++){
            if(i==0){
                sb.append(nums[i]);
            }else{
                sb.append(" ");
                sb.append(nums[i]);
            }
        }
        String answer = sb.toString();
        System.out.println("返回值:" + answer);
        return answer;


    }

    public static void main(String[] args) {
        // You can add more test cases here
        int[] sums1 = {1269, 1160, 1663};
        int[] sums2 = {1, 1, 1};
        int[] sums3 = {226, 223, 225, 224, 227, 229, 228, 226, 225, 227};
        int[] sums4 = {-1, 0, -1, -2, 1, 0, -1, 1, 0, -1};
        int[] sums5 = {79950, 79936, 79942, 79962, 79954, 79972, 79960, 79968, 79924, 79932};

        //solution(3,sums1);
        System.out.println(solution(3, sums1).equals("383 777 886"));
        System.out.println(solution(3, sums2).equals("Impossible"));
        System.out.println(solution(5, sums3).equals("111 112 113 114 115"));
       System.out.println(solution(5, sums4).equals("-1 -1 0 0 1"));
        System.out.println(solution(5, sums5).equals("39953 39971 39979 39983 39989"));
    }
}

整个过程可以分为几个关键步骤:

1. 排序和数组

接下来,代码对输入的和数组进行排序。排序的目的是为了方便后续的推导过程,确保和数组中的元素按照从小到大的顺序排列。排序后的和数组有助于简化推导过程中的数学计算,让sums的数严格遵守 1+2 、1+3、1+4、1+5…… 2+3、2+4……3+4……(里面的数字代表答案数字的序号)使得推导过程更加直观和高效。

2. 推导第二个数和第三个数的关系

在排序后的和数组基础上,通过计算前两个和的差值,这里可以选择任何可以得到一个两个数关系的数推导,为了方便,我们选择sums头两个推导出第三个数和第二个数的差值。然后,利用sums第n-1个是第二个数和三个数的和解方程可以得到这两个数。接着,通过第一个和与第二个数,推导出第一个数。这一步是整个推导过程的关键,得到第一个数之后就可以在sums前n个元素里面算出其他数。

3. 推导剩余的数

在得到前两个数之后,代码通过循环推导剩余的数。在循环中,代码检查当前和是否与前一个和相同,如果是,则跳过该和,以避免重复计算。如果循环结束后仍未推导出所有数,则返回“Impossible”。这一步确保了推导过程的完整性和正确性。

4. 验证推导结果

推导出所有数之后,代码生成一个验证数组,其中包含所有两两元素之和。然后对验证数组进行排序,并与原始的和数组进行比较。如果两者完全一致,则说明推导出的数组是正确的;否则,返回“Impossible”。这一步是验证推导结果的关键,确保推导出的数组满足给定的和数组。

5. 返回结果

最后,代码将推导出的数组转换为字符串格式,并返回结果。这一步确保了结果的输出格式符合预期。

不足

在利用第一个数推导其他数时,对解的结果里会出现很多重复的数的情况处理能力有限:

for(int i=1;res<n;i++){
            //TODO:这里用while过不了,需要用if!!不能确定重复的解有多少个,如果题目的重复解出现两个以上就不能通过
            if(i<sums.length&&sums[i]==sums[i-1]){
                i++;
            }
            if(i== sums.length){
                return "Impossible";
            }
            nums[res]=sums[i]-one;
            res++;
        }