Day9 - E - Max Sum Plus Plus HDU - 1024

Now I think you have got an AC in Ignatius.L's "Max Sum" problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now you are faced with a more difficult problem.

Given a consecutive number sequence S  1, S  2, S  3, S  4 ... S  x, ... S  n (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ S  x ≤ 32767). We define a function sum(i, j) = S  i + ... + S  j (1 ≤ i ≤ j ≤ n).

Now given an integer m (m > 0), your task is to find m pairs of i and j which make sum(i  1, j  1) + sum(i  2, j  2) + sum(i  3, j  3) + ... + sum(i  m, j  m) maximal (i  x ≤ i  y ≤ j  x or i  x ≤ j  y ≤ j  x is not allowed).

But I`m lazy, I don't want to write a special-judge module, so you don't have to output m pairs of i and j, just output the maximal summation of sum(i  x, j  x)(1 ≤ x ≤ m) instead. ^_^

InputEach test case will begin with two integers m and n, followed by n integers S 1, S 2, S 3 ... S n.
Process to the end of file.
OutputOutput the maximal summation described above in one line.
Sample Inputhtml

1 3 1 2 3
2 6 -1 4 -2 3 -2 3

Sample Outputios

6
8


        
 

Hint數組

Huge input, scanf and dynamic programming is recommended.

思路:最大m段子段和問題,設sum[n][m]爲前n個數,m段子段和的最大值,狀態轉移有三:
1.a[n]不屬於該第m段最大和,sum[n][m] = sum[n-1][m]
2.a[n]屬於該第m段最大和,且爲新的一段 sum[n][m] = sum[n-1][m-1] + a[n]
3.a[n]屬於該第m段最大和,且不爲新的一段 sum[n][m] = sum[n-1][m] + a[n]
由狀態轉移方程易知,1與3互相矛盾,就要尋找第二個狀態轉移方程來輔助計算,令b[n][m]爲包含a[n]的前n個數的m段最大子段和,則sum[n][m]爲前n個數的m段最大子段和,a[n]不必定包含在內,據此知,sum[n][m] = max(b[j][m])(m<=j<=n),最終答案就是sum[n][m]
由上述分析,可得出b[n][m]的狀態轉移方程,a[n]屬於第m段/不屬於第m段,即b[n][m] = max(b[n-1][m], sum[n-1][m-1]) + a[n],經過該式能夠得出計算順序,先計算b[n][m],其中須要sum[n-1][m-1]與b[n-1][m],再更新sum[n][m]),而在計算sum[n][m] = max(b[j][m])(n<=j<=m),類比徹底揹包的優化,在計算sum[n][m]時,sum[n][m] = max(b[j][m])(m<=j<=n) = max(b[n][m], sum[n-1][m]),由於sum[n-1][m] = max(b[x][m])(m<=x<=n-1),等式成立,咱們枚舉的順序是從小到大,因此在sum[n][m]計算以前sum[n-1][m]已經計算好了,不須要再重複枚舉了
const int maxm = 1005;

int sum[maxm][maxm], b[maxm][maxm], a[maxm];

int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int n, m;
    int tmp;
    while(cin >> m >> n) {
        memset(sum, 0, sizeof(sum)), memset(a, 0, sizeof(a)), memset(b, 0, sizeof(b));
        for(int i = 1; i <= n; ++i) cin >> a[i];
        for(int i = 1; i <= m; ++i) {
            for(int j = i; j <= n; ++j) {
                b[j][i] = max(b[j-1][i], sum[j-1][i-1]) + a[j];
                sum[j][i] = max(sum[j-1][i], b[j][i]);
            }
        }
        cout << sum[n][m] << endl;
    }
    return 0;
}
可是注意,該題的範圍是1e6,二維顯然空間炸了,就須要優化爲一維的滾動數組形式,咱們將計算的式子都列出來:
b[n][m] = max(b[n-1][m], sum[n-1][m-1]) + a[n], sum[n][m] = max(sum[n-1][m], b[n][m])
滾動時從小到大,該位置爲j,則j以前的是[][m],j以後的是[][m-1](上一輪的),則先計算b[],再更新sum[],由於sum和b有衝突,先sum[n-1][m-1],再sum[n-1][m],就用一個tmp來記錄
const int maxm = 1e6+5;

int sum[maxm], b[maxm], a[maxm];

int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int n, m;
    int tmp;
    while(cin >> m >> n) {
        memset(sum, 0, sizeof(sum)), memset(a, 0, sizeof(a)), memset(b, 0, sizeof(b));
        for(int i = 1; i <= n; ++i) cin >> a[i];
        for(int i = 1; i <= m; ++i) {
            tmp = -0x3f3f3f3f;
            for(int j = i; j <= n; ++j) {
                b[j] = max(b[j-1], sum[j-1]) + a[j]; // 此處sum[j-1] 是第i-1段
                sum[j-1] = tmp;  // 更新sum[j-1] 爲第i段的, 該tmp是第i段的j-1,由上一次的循環繼承而來
                tmp = max(sum[j-1], b[j]); // 更新tmp, 該tmp是第i段的j,爲下一次循環中的的j-1
            }
            sum[n] = tmp;
        }
        cout << sum[n] << "\n";
    }
    return 0;
}
 

 參考微博:https://www.cnblogs.com/chuckcpc/p/dp_Max_M_Sum.html優化

相關文章
相關標籤/搜索