Time Limit: 1 second
Memory Limit: 128 MB
【问题描述】
N个数排成一排,你可以任意选择连续的若干个数,算出它们的和。问该如何选择才能使得和的绝对值最小。如:N=8时,8个数如下:
1 2 3 4 5 6 7 8
-20 90 -30 -20 80 -70 -60 125
如果我们选择1到4这4个数,和为20,还可以选择6到8这3个数,和为-5,|-5|=5,该方案获得的和的绝对值最小。
【输入格式】
第一行输入N,表示数字的个数。接下来N行描述这N个数字。
【输出格式】
第一行输出一个整数,表示最小绝对值的和,第二行包含两个整数表示形成该绝对值和最长序列的长度。
【数据规模】
40%的数据 N<=4000 对于许多数据,最长序列的长度唯一。 100%的数据 N<=100000,|每个数字的值|<=10^10
Sample Input1
20 12 -220 -195 -316 -300 668 576 1034 1055 972 -1023 866 -1469 492 857 1954 1685 1046 -1748 -1480
Sample Output1
12 1
【题解】
对于区间的静态查询。要用前缀和的方法。
sum[i]=sum[i-1]+data[i];
然后处理出sum[0..n]注意要把sum[0]也处理出来。
然后把这n+1个sum从小到大或从大到小排序。排序完之后。可以保证序列最小和
就是min{abs(sum[i]-sum[i-1])}其中i∈(1..10);
因为排序之后相邻的sum值是最接近的。
然后要记录sum对应的i。
最大sum相同时。就让他们的差的绝对值最大就好。
如果全是正数则不用排序。因为sum会越来越大。。这样。
用__int64之后oj才让过了。。
【代码】
#include <cstdio> #include <algorithm> using namespace std; struct node //pos用于记录是哪一个位置的前缀和。 { __int64 what; int pos; }; int n,maxl; __int64 x, minn; node sum[100001]; //用于存储前缀和。 void input_data() { scanf("%d", &n); sum[0].what = 0; //前0个元素的和也要加进去。 sum[0].pos = 0; //不然没法单独取到第一个元素。 for (int i = 1; i <= n; i++) //输入n个数字记录前缀和 { scanf("%I64d", &x); //这是__int64的输入格式。 sum[i].what = sum[i - 1].what + x; sum[i].pos = i; } } int cmp(const node &a, const node &b) //sort函数的对比函数 { if (a.what < b.what) return 1; return 0; } int abs(int x) //返回x的绝对值。 { return x < 0 ? -x : x; } void get_ans() { minn = sum[1].what; //一开始先处理最小值是第一个。 if (minn < 0) minn = -minn; maxl = sum[1].pos; //记录下他的长度。 for (int i = 2; i <= n; i++) //枚举每两个前缀和的差的绝对值 { __int64 temp = sum[i].what - sum[i - 1].what; int temp2 = abs(sum[i].pos - sum[i - 1].pos); if (temp < minn) //尝试更新最小值。 { minn = temp; maxl = temp2; } else //如果相同则看是否长度更长 if (temp == minn && temp2 > maxl) maxl = temp2; } } void output_ans() { printf("%I64d ", minn); printf("%d", maxl); } int main() { //freopen("F:\rush.txt", "r", stdin); //freopen("F:\rush_out.txt", "w", stdout); input_data(); sort(sum + 0, sum + 1+n, cmp);//要对sum[0..n]都进行排序。 get_ans(); output_ans(); //fclose(stdin); //fclose(stdout); return 0; }