问题描述
小S拥有三个正整数:L、R 和 S。他想知道能否从 L 到 R(包含 L 和 R)之间选择一些整数,使它们的总和正好等于 S。
请注意,从 L 到 R 的每个整数最多只能选择一次。如果可以找到满足条件的整数集合,请返回 1,否则返回 0。
例如:当 L = 5,R = 8,S = 12 时,可以选择整数 5 和 7,因为它们的和是 12,所以答案为 1。
测试样例
样例1:
输入:
L = 5,R = 8,S = 12
输出:1
样例2:
输入:
L = 3,R = 10,S = 17
输出:1
思路: 首先我们注意到[L,R]是一个连续的区间,我们要在里面选出几个不重复的数而且这些数加起来刚好等于S。我们不知道需要几个数的和,并且这几个数是怎么分布的。比如1、2、3、4、5、6、7、8、9、10需要找到和是15的数,可以是2个数的和(5、10)、(7、8)、(6、9)也可以是5个数(1、2、3、4、5)。就上面1到10的例子,我们可以知道n个数最小值是前面n个数的和,最大值是后面n个的和,然后因为L到R是连续的,所以得到的n个数的最小值到最大值也是连续的,我们可以在这些数里面随便找n个数结果肯定落在这个区间里面。
所以可以创建两个数组来分别记录这些数的和,small[i],记录了L从L开始连续i+1个数的和,big[i]记录了L+i到R这些数的和。
public class 区间内选择整数求和 {
public static int solution(int L, int R, int S) {
int max=(L+R)*(R-L+1)/2;
//如果全部加起来还没他大,那么就必定不会超过他
if(max<S){
return 0;
}
int length=R-L+1;
int[] small=new int[length];
int[] big=new int[length];
int i;
small[0]=L;
big[length-1]=R;
for(i=1;i<length;i++){
small[i]+=small[i-1]+L+i;
}
for(i=length-2;i>=0;i--){
//注意i是递减的,他和R的规律
big[i]=big[i+1]+L+i;
}
//得到的是n。。。3、2、1个数相加的和,需要反转
i=0;
int j=length-1,tmp;
while(i<j){
tmp=big[i];
big[i]=big[j];
big[j]=tmp;
i++;
j--;
}
//System.out.println(Arrays.toString(small));
//System.out.println(Arrays.toString(big));
//看看数落在哪几个数的和的区间,最后一个一开始判断了,所以这里不用判断
for(i=0;i<length-1;i++){
if(S>=small[i]&&S<=big[i]){
return 1;
}
}
return 0;
}
public static void main(String[] args) {
//System.out.println(solution(5, 8, 12));
System.out.println(solution(5, 8, 12) == 1);
System.out.println(solution(3, 10, 17) == 1);
System.out.println(solution(1, 5, 20) == 0);
}
}
代码首先计算区间内所有整数的最大和,若该和小于S,则直接返回0。接着,通过两个数组small和big分别记录从区间两端开始的连续整数和。small数组从L开始累加,big数组从R开始累加,并在最后进行反转,以便与small数组对应。最后,遍历这两个数组,判断S是否在某个连续整数和的区间内。若存在,则返回1;否则返回0。