1065 最小正子段和 二分答案 + 判定

Posted stupid_one

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1065 最小正子段和 二分答案 + 判定相关的知识,希望对你有一定的参考价值。

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1065

我的思路比较笨,我是直接二分那个答案mid

然后进行一次O(nlogn)的判定,如果能找到一个区间的和比mid小的,(当然这个区间的和也是要大于0),那就return true

 

进行判断如下:

处理出前缀和dp[i],对于每一个i

目标是:在前i - 1个中,查看是否有这样的一个x,使得,dp[i] - x    <=   mid,&& dp[i] - x >= 1

二分找x,把前i - 1个东西压去set中,然后二分找x >= dp[i] - mid的那个x,判定即可。

 

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define ios ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset>
const int maxn = 50000 + 20;
LL dp[maxn];
int n;
set<LL>ss;
bool check(LL val) {
    if (dp[1] <= val && dp[1] >= 1) return true;
    ss.clear();
    ss.insert(0);
    ss.insert(dp[1]);
    LL mx = max(0LL, dp[1]);
    for (int i = 2; i <= n; ++i) {
        LL x = dp[i] - val;
        if (mx < x) {
            mx = max(mx, dp[i]);
            ss.insert(dp[i]);
            continue;
        }
        set<LL> :: iterator it = ss.lower_bound(x);
        if (dp[i] - *it >= 1) return true;
        mx = max(mx, dp[i]);
        ss.insert(dp[i]);
    }
    return false;
}
void work() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        int x;
        scanf("%d", &x);
        dp[i] = dp[i - 1] + x;
    }
//    for (int i = 1; i <= n; ++i) {
//        cout << dp[i] << " ";
//    }
//    cout << endl;
    LL be = 1, en = 1e18L;
//    cout << check(1) << endl;
    while (be <= en) {
        LL mid = (be + en) >> 1;
        if (check(mid)) {
            en = mid - 1;
        } else be = mid + 1;
    }
//    cout << be << endl;
    printf("%lld\\n", be);
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    work();
    return 0;
}
View Code

 

这题的正解是:

预处理前缀和,然后按从小到大排序,相同时再按位置从小到大排序

比如前缀和是,3、1、2

排序后是1(2)、2(3)、3(1),括号的是位置

那么3不能作为贡献,因为他前面没有比他位置小的数字。

一开始以为这样找也是O(n^2),但是发现好像只需要比较相邻的两位就可以,

给个数据

4

-1、-1、-1、8

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset>
const int maxn = 50000 + 20;
struct node {
    LL val;
    int pos;
    bool operator < (const struct node & rhs) const {
        if (val != rhs.val) return val < rhs.val;
        else return pos < rhs.pos;
    }
}a[maxn];
void work() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        int x;
        scanf("%d", &x);
        a[i].val = a[i - 1].val + x;
        a[i].pos = i;
    }
    sort(a + 1, a + 1 + n);
    LL ans = 1e18L;
    if (a[1].val >= 1) ans = a[1].val;
    for (int i = 2; i <= n; ++i) {
        if (a[i].val >= 1) ans = min(a[i].val, ans);
        if (a[i].pos > a[i - 1].pos && a[i].val - a[i - 1].val >= 1) {
            ans = min(ans, a[i].val - a[i - 1].val);
        }
    }
    cout << ans << endl;
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    work();
    return 0;
}
View Code

 

以上是关于1065 最小正子段和 二分答案 + 判定的主要内容,如果未能解决你的问题,请参考以下文章

51Node 1065----最小正子段和

AC日记——最小正子段和 51nod 1065

51Nod1065 最小正子段和

51 Nod 1065 最小正子段和(前缀和)

51nod 1065 最小正子段和

51nod-1065:最小正子段和(STL)