思路: 对于无序得数组我们先对他进行从大到小排序,使他变得有序。于是我们就得到一个递增得序列,假设我们要求的做加法运算的数也是有序的那么 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++;
}