CF E

题目:传送门

题意

给你一个长度为 n 的序列 a,你有两种操作:

1.选择一段区间 [l, r] 满足区间内所有数都大于 0,对区间内每个数减 1

2.选择一个 i 和 x,让 a[i] = a[i] - x

问最少需要操作多少次,使得序列所有数都变为 0

1 <= n <= 5000; 0 <= ai <= 1e9

思路

一. DP

dp[i][j]:处理完序列前 i 个数,用了 j 次操作 1 的答案

#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define UI unsigned int
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF 0x3f3f3f3f
#define inf LLONG_MAX
#define PI acos(-1)
#define fir first
#define sec second
#define lb(x) ((x) & (-(x)))
#define dbg(x) cout<<#x<<" = "<<x<<endl;
using namespace std;

const int N = 1e6 + 5;

int n;

int dp[5005][5005];

int a[5005];

void solve() {

    scanf("%d", &n);

    rep(i, 1, n) scanf("%d", &a[i]);

    mem(dp, INF);

    rep(i, 1, n) dp[0][min(a[i], i)] = min(a[i], i);

    dp[0][0] = 0;

    rep(i, 1, n) {

        rep(j, 0, n) {

            dp[i][min(a[i], j)] = min(dp[i][min(a[i], j)], dp[i - 1][j] + 1);

            if(a[i] <= n) {

                if(a[i] <= j) {

                    dp[i][a[i]] = min(dp[i][a[i]], dp[i - 1][j]);

                }

                else {

                    dp[i][a[i]] = min(dp[i][a[i]], dp[i - 1][j] + a[i] - j);

                }

            }

        }

    }

    int ans = INF;

    rep(i, 0, n) ans = min(ans, dp[n][i]);

    printf("%d
", ans);

}


int main() {

//    int _; scanf("%d", &_);
//    while(_--) solve();

    solve();

    return 0;
}
View Code

二.分治O(n^2)

#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define UI unsigned int
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF 0x3f3f3f3f
#define inf LLONG_MAX
#define PI acos(-1)
#define fir first
#define sec second
#define lb(x) ((x) & (-(x)))
#define dbg(x) cout<<#x<<" = "<<x<<endl;
using namespace std;

const int N = 1e6 + 5;

const LL mod = 1e9 + 7;

int n;

LL a[N];

LL cal(int l, int r) {

    if(l > r) return 0;

    if(l == r) return min(a[l], 1LL);

    LL mi = a[l]; int pos = l;

    rep(i, l, r) {

        if(a[i] < mi) {

            mi = a[i];

            pos = i;

        }

    }

    rep(i, l, r) {

        a[i] -= mi;

    }

    return min(cal(l, pos - 1) + cal(pos + 1, r) + mi, 1LL * (r - l + 1));

}

void solve() {

    scanf("%d", &n);

    rep(i, 1, n) scanf("%lld", &a[i]);

    printf("%lld
", cal(1, n));

}


int main() {

//    int _; scanf("%d", &_);
//    while(_--) solve();

    solve();

    return 0;
}
View Code

三.分治O(nlogn),线段树优化

#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define UI unsigned int
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF 0x3f3f3f3f
#define inf LLONG_MAX
#define PI acos(-1)
#define fir first
#define sec second
#define lb(x) ((x) & (-(x)))
#define dbg(x) cout<<#x<<" = "<<x<<endl;
using namespace std;

const int N = 1e6 + 5;

const LL mod = 1e9 + 7;

int n;

int a[N];

struct note {

    int x, lz, id;

}t[N];

void build(int rt, int l, int r) {

    if(l == r) {

        t[rt].x = a[l];

        t[rt].id = l;

        t[rt].lz = 0;

        return ;

    }

    int mid = (l + r) >> 1;

    build(rt << 1, l, mid);

    build(rt << 1 | 1, mid + 1, r);

    if(t[rt << 1].x <= t[rt << 1 | 1].x) {

        t[rt].x = t[rt << 1].x;

        t[rt].lz = t[rt << 1].lz;

        t[rt].id = t[rt << 1].id;

    }

    else {

        t[rt].x = t[rt << 1 | 1].x;

        t[rt].lz = t[rt << 1 | 1].lz;

        t[rt].id = t[rt << 1 | 1].id;

    }

}

void pushdown(int rt) {

    if(t[rt].lz) {

        t[rt << 1].x -= t[rt].lz;

        t[rt << 1 | 1].x -= t[rt].lz;

        t[rt << 1].lz = t[rt].lz;

        t[rt << 1 | 1].lz = t[rt].lz;

        t[rt].lz = 0;

    }

}

note query(int rt, int l, int r, int L, int R) {

    if(L <= l && r <= R) return t[rt];

    pushdown(rt);

    int mid = (l + r) >> 1;

    note ans;

    ans.x = INF; ans.id = 0; ans.lz = 0;

    if(L <= mid) {

        note tmp = query(rt << 1, l, mid, L, R);

        if(tmp.x < ans.x) ans.x = tmp.x, ans.id = tmp.id;

    }

    if(R > mid) {

        note tmp = query(rt << 1 | 1, mid + 1, r, L, R);

        if(tmp.x < ans.x) ans.x = tmp.x, ans.id = tmp.id;

    }

    return ans;

}

void update(int rt, int l, int r, int L, int R, int d) {

    if(L <= l && r <= R) {

        t[rt].x -= d;

        t[rt].lz += d;

        return ;

    }

    pushdown(rt);

    int mid = (l + r) >> 1;

    if(L <= mid) update(rt << 1, l, mid, L, R, d);

    if(R > mid) update(rt << 1 | 1, mid + 1, r, L, R, d);

    if(t[rt << 1].x <= t[rt << 1 | 1].x) {

        t[rt].x = t[rt << 1].x;

        t[rt].id = t[rt << 1].id;

    }

    else {

        t[rt].x = t[rt << 1 | 1].x;

        t[rt].id = t[rt << 1 | 1].id;

    }

}

int cal(int l, int r) {

    if(l > r) return 0;

    if(l == r) {

        note now = query(1, 1, n, l, r);

        if(now.x) return 1;

        else return 0;

    }

    note now = query(1, 1, n, l, r);

    update(1, 1, n, l, r, now.x);

    return min(cal(l, now.id - 1) + cal(now.id + 1, r) + now.x, (r - l + 1));

}

void solve() {

    scanf("%d", &n);

    rep(i, 1, n) scanf("%d", &a[i]);

    build(1, 1, n);

    printf("%d
", cal(1, n));

}


int main() {

//    int _; scanf("%d", &_);
//    while(_--) solve();

    solve();

    return 0;
}
View Code

 

原文地址:https://www.cnblogs.com/Willems/p/13582611.html