HDU 2058The sum problem(详细解答原理!!!

The sum problem

Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 39373 Accepted Submission(s): 11831

Problem Description
Given a sequence 1,2,3,…N, your job is to calculate all the possible sub-sequences that the sum of the sub-sequence is M.

Input
Input contains multiple test cases. each case contains two integers N, M( 1 <= N, M <= 1000000000).input ends with N = M = 0.

Output
For each test case, print all the possible sub-sequence that its sum is M.The format is show in the sample below.print a blank line after each test case.

Sample Input

20 10
50 30
0 0

Sample Output

[1,4]
[10,10]

[4,8]
[6,9]
[9,11]
[30,30]

首先千万不要使用二重循环,你已提交保证超时,无论你怎么改还是超时,本人亲自体验过!

这道题使用的是数学知识
但是很多博主讲的也不详细,晦涩难懂!因为他们用的标识符自己懂就ok,标识符也未必知名其意
这里我详细讲解一下

首先输入N,M
初看题目可能存在M<=N,M>=N但N数和小于M,或者M>=N但N 数和大于M总共三种情况!

但是下面的讲解的都会把这几种情况包括进来(下面讲解时n表示N,m表示M)

1 2 3 4 5 6…N

等差数列n项和公式为 n* a1+n*(n-1)/2 (公式0)

假如从1开始,n项和为n+n*(n-1)/2 = n*(n+1)/2

令n*(n+1)/2=m n*(n+1)=2m

n = sqrt(2m) 先假如n是浮点数,那么 (sqrt(2m))(sqrt(2m)+1)>2m
sqrt(2m) * sqrt(2m)<2m
那么n是整数时(即 n=(int)sqrt(2
m))的时候,n n<=2m 是绝对小于2m的
为什么可以等于呢?
比如m=10,n=4,刚好歪打正着呗

那么求出来的n是什么意思呢?
假如m=10,此时n=4
n是1 2 3 4 这四个数是最接近10的,(该例子刚好等于10,可以自己尝试m=12时的情况)

其实n也可以理解为子序列最大的项数,其中1…n是最小的四项元素数,因为刚好从1开始算起

现在说明另一个公式
*子序列元素和 (首元素+末元素)项数个数/2

那么对于现在该序列首元素也可以说首位置吧(从1开始,不是从0开始哦!)

现在i作为首元素,项数为n
那么有(i + i+n-1)*n /2
比如 1 2 3 4 =10 或者 4 5 6 7 8 =30
自己将i=1,n=4代入和i=4,n=5代入加深理解

既然子序列和等于(2*i+n-1)n/2
表明(2
i+n-1)n/2 = m (公式1)
那么 i=((2
m)/n -n +1)/2 (公式2)(公式1反推)

现在看代码就更容易理解我所讲的,我会每句都会讲解的,要耐心看!一定要把它弄懂

#include<iostream>
#include<cmath>
using namespace std;
//(i+i+n-1)*n/2=m;
//i=(2*m/n-n+1)/2
int main()
{
	int N,M;
	while(cin>>N>>M&&(N||M))
	{
		int len = (int)sqrt(2*M);
len其实就是最大的项数(即子序列个数的最大值),你想想

我从1开始不断的+2+3+....k,假如刚好k位置时即从1到k的所有元素和加起来刚好
小于M,在1到k+1就大于M,那么len就是子序列个数的最大值,不可能还超过len的,
既然len是最大的子序列个数,但是从1到len的这几位数加起来是最小的,
可能小于M(比如m=12的时候,此时就需要往前移,比如 2 3 4 5 ,若还小于
那就继续移 3 4 5 6 
(**在这个移的过程中就相当于在找首元素 i ,下面解释如何找i,肯定不是循环)**。
可能移某位置大于m的时候还是不满足(比如m=121 2 3 4 是小于m的,
2 3 4 5是大于m的)那么接下来就是在三个子序列数(子序列个数为3)来找
因为四个是不可能的,因为越往后越大,当进行三个子序列找
的时候,就需要找首元素,一旦首元素找到,后面的元素就立马确定
当然不是循环来找,那估计可能又会超时!!!嘿嘿

		int n,i;//n就是子序列个数,i是首元素
		for(n=len;n>=1;n--)//子序列个数不断减少直到1
		{我可以直接说很多用到这个循环到1的都是这个原理
//既然子序列和等于(2*i+n-1)*n/2
//表明(2*i+n-1)*n/2 = m   (公式1)
//那么 i=((2*m)/n -n +1)/2    (公式2)

			i=(2*M/n-n+1)/2;当然找这个首元素也可以多总找法
//根据公式2反推首元素(对于每个子序列个求i首元素)

			if((2*i+n-1)*n==2*M)这里判断相等也可以使用 公式0
			{
	//这里为什么会判断啦?
//因为计算i的过程中并不是两个整数除以整数等于整数,其实是有小数的
//只是int隐式转化为整数,如果两次的整数都存在这种情况,说明这个求出来
//i再进行计算是不满足公式1的,只有两次除法的时候都是刚好没有
//小数点(也就是两个数除余等于0)
				cout<<'['<<i<<','<<i+n-1<<']'<<endl;
				}
		}
		cout<<endl;
	}
	return 0;
}
最后,要自己拿数据尝试,自己动手在纸上计算。我觉得我讲的够详细啦!
我把我的理解讲出来啦!我尽力啦,毕竟我不是老师,哈哈哈!!!

我觉我很啰嗦!但是又怕没讲清楚,希望对大家理解有帮助
总的来说,万变不离其宗,重点是get到里面的点就明白啦!

当然如果理想直到二重循环怎么写?
请参考下部分代码

#include<iostream>
using namespace std;
int main()
{
	int N,M;
	while(cin>>N>>M&&(N||M))
	{
		int i,j,k;
		int value;
		int sum;
		for(i=1;i<=N;i++)
		{
			value=i;
			sum=0;
			for(j=value;j<=M&&j<=N;j++)
			{
				sum+=j;
				if(sum>M)
				{
					if(sum-j==M)
					{
						printf("[%d,",value);
						printf("%d]\n",j-1);
					}
				}
			}
		}
		if(M<=N)
			printf("[%d,%d]\n",M,M);
		printf("\n");
	}
	return 0;
}

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值