码迷,mamicode.com
首页 > 其他好文 > 详细

51nod 1065 最小正字段和 解决办法:set存前缀和,二分插入和二分查找

时间:2017-10-05 10:56:53      阅读:228      评论:0      收藏:0      [点我收藏+]

标签:nbsp   namespace   运算   超时   upper   iter   51nod   names   更新   

题目:

技术分享

 

这题要求大于0的最小字段和,常规O(n)求最大字段和的方法肯定是没法解的。

我的解法是:用sum[i]存前i项的和,也就是前缀和。

      这题就变成了求sum[j]-sum[i]的大于0的最小值( j > i )。

      我们可以看到直接循环运算量是50000*50000,会超时。

      所以我们应该充分利用前缀和的特性。

      我们用一个set容器来装sum。

      当算到i项时,保证前i-1项已经装入了set中。

      我们用二分查找找到第一个比sum[i]小的值,用sum[i]减去这个值来更新答案。

      至于二分插入,set容器中插入数据用的就是二分插入。

 

代码:

#include <bits\stdc++.h> 
using namespace std;
typedef long long ll;
ll sum[50010];  // sum[i]表示 1~(i-1) 项的和 
set <ll> s;     // 到第i项时,s存的是 sum[1]~sum[i-1] 
set <ll>::iterator it;  //迭代器 

int main() {
  int n;
  cin >> n;
    int mn = 2000000000;
    int key;
  for(int i = 1;i <= n; i++){
      cin >> key;
      sum[i] = sum[i-1]+key;
    }
    
    s.insert(0);
    for(int i = 1;i <= n; i++){
        // lower_bound返回大于等于sum[i]的最小值
        // upper_bound返回大于sum[i]的最小值 
        it = s.lower_bound(sum[i]);
        if(it != s.begin()){ 
            it--;
            //it表示小于sum[i]的最大值  
            if(sum - *it > 0){
                mn = min((ll)mn,sum[i]-*it);
            }
        } 
        s.insert(sum[i]);
    }
    cout << mn << endl;
  return 0;
}

 

51nod 1065 最小正字段和 解决办法:set存前缀和,二分插入和二分查找

标签:nbsp   namespace   运算   超时   upper   iter   51nod   names   更新   

原文地址:http://www.cnblogs.com/zhangjiuding/p/7628119.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!