1. 程式人生 > >一道貪心:加括號使算式的值最大

一道貪心:加括號使算式的值最大

問題描述

給定一個算術表示式形如1+3-5-4+6,表示式中的運算數全部都是正數,運算子全部是加號或者減號。
現在可以給算術表示式加任意多的括號,使得表示式的值最大。
如對於1+3-6-9+4-5-7+8,可以1+3-(6-9)+4-(5-7)+8,最優的方案是1+3-(6-9+4-5-7)+8

資料格式

T 例子個數
n1 第一個例子的運算數個數
1+3-6-9+4-5-7+8 算數表示式
n2
......

輸出一個數字,表示表示式的最大值。
資料範圍:運算數個數為1e5。

解析

  • 最優答案中,括號只加在減號前面
  • 最優答案中,若干個加號之間不加括號,例如3-4+5+6+7-8,其中5,6,7之間肯定沒有括號
  • 最優答案中,括號肯定不會巢狀
  • 最優答案中,形如-a+b-c,如果c<b,那麼-(a)+b-(c 比-(a+b-c 結果要好。

貪心的原則就是,既然無論如何都要給右面留下左括號,那麼左括號的位置必然是最優的。

程式碼

#include<iostream>
using namespace std;
const int maxn = 1e5 + 7;
typedef  long long ll;
int a[maxn];
char op[maxn];
int m;
int find(int ind) {
    for (int i = ind; i < m; i++) {
        if ( op[i] == '+' &&(i >= m - 1 || a[i + 1] < a[i])) {
            return i-1;
        }
    }
    return m - 1;
}
ll sum_range(int f, int t) {
    ll s = 0;
    for (int i = f; i <= t; i++) {
        int value = (op[i] == '+' ? 1 : -1)*a[i];
        s -= value; 
    }
    return s;
}
ll solve() {
    //壓縮正號
    int i = 0;
    for (int j = 1; j < m; j++) {
        if (op[j] == '+') {
            if (op[i] == '+')
                a[i] += a[j];
            else {
                i++;
                op[i] = '+';
                a[i] = a[j];
            }
        }
        else {
            i++;
            op[i] = '-';
            a[i] = a[j];
        }
    }
    m = i + 1; 
    ll s = 0;
    //如果我後面的負數比我大,我就要犧牲
    for (int i = 0; i < m; i++) { 
        if (op[i] == '-') {
            int j = find(i);
            s += -a[i]+sum_range(i+1, j); 
            i = j;
        }
        else {
            s += a[i];
        } 
    }
    return s;
}
int main() {
    freopen("in.txt", "r", stdin);
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> m;
        op[0] = '+';
        for (int j = 0; j < m; j++) {
            if (j > 0) {
                cin >> op[j];
            }
            cin >> a[j];
        }
        cout << solve() << endl;
    }
    return 0;
}