和为s的连续正数序列
题目描述
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例:
输入:target = 9
输出:[[2,3,4],[4,5]]
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
提示:
1 <= target <= 10^5
题目分析
一开始想这题是关于数组的求和,很有可能是和动态规划有关,考点就是通过动态规划来存放中间计算量,来达到加速效果,不然会卡时间超限,嗯我真是天才,一看题目什么都知道了;
然后就秒想出状态转移方程sum[i][j] = sum[i-1][j-1] + j,时空都是O(N^2),然后再天才的千方百计将空间复杂度下降到O(N),并添加一些提前结束计算循环的剪枝,再按照题目要求输出,才提交上去,果然AC了!只不过...
我真是天才,超越Kotlin100%的用户,但是感觉不太对劲啊,怎么花了差不多四秒才执行完,遂用Java重新写了一遍,提交上去,啊哦~
我真是个弟弟,然后我就重新想,这道题的第I部分是前后夹击,但是这道题这样做不行,因为要求是连续和,不能前后计算,因为连续和为target的几个数(两个以上),最大的只能是target/2,所以就从target/2往前推,一开始设置尾指针tail为target/2,头指针head为target/2-1,长度len为2,和sum为head+tail,就开始下面的循环
sum为target,就将从head开始,包装出一个长度为len的步长为1的单增数组,并添加到答案,重点来了,因为从head到tail的和为target,那么,tail就肯定不能出现在长度len更长的其他答案里面(反证法,如果在,和就肯定比target大),tail就需要往前推,tail往前推了,head+tail肯定就小于target了,所以head也往前推,sum就比原来小了len那么多
sum比target大,那tail肯定要排除在后面的答案序列里面,举个例子,2+3+4比8要大,我们是从后往前的,如果不把4踢掉,那么连续序列肯定比(2+3+4)要长,肯定不符合答案,所以这种情况下前后指针都往前推,sum就比原来小了len那么多
sum比target小,这种情况只能head往前推,不能head和tail都往前推,因为head to tail比target小,都往前推的话还是比target小,没意义,只能通过增长序列来求解,所以这种情况head往前推,sum+=head,len++
ArrayList<int[]> ans = new ArrayList<>();
int tail = (target % 2 == 0) ? target / 2 : target / 2 + 1;
int head = tail - 1;
int len = 2;
int sum = tail + head;
while(head != 0){
if(sum == target){
int[] tmp = new int[len];
for (int i = 0; i < tmp.length; i++) {
tmp[i] = head + i;
}
ans.add(tmp);
tail--;
head--;
sum -= len;
}else if(sum > target){
head--;
tail--;
sum -= 2;
}else{
head--;
len++;
sum += head;
}
}
int[][] ret = new int[ans.size()][];
for (int i = 0; i < ans.size(); i++) {
ret[i] = ans.get(ans.size() - i - 1);
}
return ret;