1. 程式人生 > 實用技巧 >51nod 1065 最小正子段和

51nod 1065 最小正子段和

題目網址:http://class.51nod.com/Challenge/Problem.html#problemId=1065

一、題目描述

N個整陣列成的序列a[1],a[2],a[3],…,a[n],從中選出一個子段(a[i],a[i+1],…a[j])

使這個子段的和>0,並且這個和是所有和>0的子段中最小的。

例如:4,-1,5,-2,-1,2,6,-2。-1,5,-2,-1,序列和為1,是最小的。

輸入

第1行:整數序列的長度N(2 <= N <= 50000)
第2 - N+1行:N個整數

輸出

輸出最小正子段和。

樣例輸入

8
4 -1 5 -2 -1 2 6 -2

樣例輸出

1

二、解題思路

這道題我們可以利用 字首和+結構體排序 來解決

首先,大家來看一下:我們假設我們先把所有數字的字首和都求出來了

(拿樣例來舉例)

prefix_sum:{0,4,3,8,6,5,7,13,11}

接下來排個序,為什麼要排序呢?

因為我們列舉每一個字首和,對當前的字首和,找到一個與它最接近的字首和,這兩者之差便有可能成為答案。

所有的字首和求出來排序,排過序之後相鄰的兩項(兩個字首和)一定是值最接近的。

在找相鄰兩項的差的時候也要注意判斷字首和的前後關係。(必須是後面的大數-前面的小數)

每個區間的和都可以看做是字首和的差。

比如這張圖,黑色是大範圍的字首和,紅色是小範圍的字首和,藍色部分的和是黑色的減去紅色的 這樣,我排序完了以後,找到兩個字首和的差是最小的,跟目前最小的比較,如果比現在最小的還小,更新最小值 提示:字首和的前後關係排序後就會打亂,建議大家記錄一下原始的下標,定義一個結構體

三、程式碼描述

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
long long n, ans=1e9;

struct node{//a[i]裡存的 
    long long sum;//到目前a[i]的字首和 
    long long key;//a[i]的原始下標 
}a[50010];

bool cmp(node x, node y){
    return x.sum < y.sum;//按照字首和升序排序 
}

int main(){
    cin >> n;
    a[
0].key = 0; a[0].sum = 0; for(int i = 1;i <= n;i++){ cin >> a[i].sum;//輸入a[i] a[i].key = i;//a[i]的原始下標為i if(i != 1){//如果至少已經有1個數了 a[i].sum += a[i-1].sum;//它的字首和為上一個數加上現在這個數 } } sort(a, a+n+1, cmp);//結構體排序 for(int i = 0;i <= n;i++){ //如果大範圍的原始下標比小範圍的原始下標要大,說明大範圍裡面是小範圍,滿足條件 //如果大範圍的字首減去小範圍的字首和大於0,滿足條件 if(a[i].key < a[i+1].key && a[i+1].sum - a[i].sum > 0){//如果滿足條件 ans = min(ans, a[i+1].sum - a[i].sum);//判斷最小值 } } cout << ans << endl; return 0; }