最小和 --- 求一类数字区间问题

先看题目:

 

1.

 

N个数排成一排,你可以任意选择连续的若干个数,算出它们的和。问该如何选择才能使得和的绝对值最小。

如:N=8时,8个数如下:

1    2    3    4    5    6    7    8

-20   90  -30   -20   80  -70  -60   125

如果我们选择144个数,和为20,还可以选择683个数,和为-5|-5|=5,该方案获得的和的绝对值最小。

输入格式:

第一行输入N,表示数字的个数。接下来N行描述这N个数字。

 

输出格式:

第一行输出一个整数,表示最小绝对值的和,第二行包含个整数表示形成该绝对值和最长序列的长度。

 

 

1<= N<=100000


 

或者

 

2.

 

N个数围成一圈,要求从中选择若干个连续的数(注意每个数最多只能选一次)加起来,问能形成的最大的和。

 

N<=100000


 

这样的题目,一般可以用前缀和来做。

 

以第一题为例子

 

我们设s[i]为前i个数字之和.那么s[i]-s[j] (i>j) 就为j+1到i的数字之和。这样我们就可以任意求出一段区间的和是多少。

 

在利用题目的条件,看看能不能利用前缀和的性质。

 

还是对于第一题。我们把得到的前缀和进行排序,那么可以知道min{ abs( s[i]-s[i-1] ) }就是答案。注意排序前加入一个s[0]=0,因为s[i]-s[j] (i>j) 表示j+1到i的数字之和

 

因为前缀和排序后。以i为一段的区间的最小绝对值一定是 min{ abs( s[n]-s[n-1] ),abs( s[n] - s[n+1] ) } 这里的n是i在排序以后的位置

 

用快排nlongn就能排好,加上O(n)的扫描。

 

下面是自己写的第一题的代码,没有测试数据(手上的测试数据有错误)。。 大家权当参考吧

 

//集训队 day4 测试数据有错 #include #include using namespace std; int _gSum[100001]; int _gPos[100001]; int tadd,tp,ti; void _fSort( int begin,int end ) { int pos = begin; for( int i = begin+1;i < end;++i ) { if( _gSum[i] < _gSum[pos] ) { tadd = _gSum[pos+1],tp = _gSum[pos],ti = _gSum[i]; _gSum[i] = tadd; _gSum[pos+1] = tp; _gSum[pos] = ti; tadd = _gPos[pos+1],tp = _gPos[pos],ti = _gPos[i]; _gPos[i] = tadd; _gPos[pos+1] = tp; _gPos[pos] = ti; ++pos; } } if( begin < pos ) _fSort( begin,pos ); if( pos < end ) _fSort( pos+1,end ); } int main() { ifstream file_in("min.in"); int n; file_in >> n; _gSum[0] = 0; int temp; for( int i = 1;i <= n;++i ) { file_in >> temp; _gSum[i] = _gSum[i-1] + temp; _gPos[i] = i; } file_in.close(); _fSort( 0,n+1 ); int min = 123456789,size;//abs int tsum; for( int i = 1;i <= n;++i ) { tsum = abs( _gSum[i] - _gSum[i-1] ); if( tsum < min ) { size = abs( _gPos[i] - _gPos[i-1] ); min = tsum; } } cout << min << endl; cout << size << endl; return 0; }  

 

 

 

你可能感兴趣的:(其他算法)