程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> HDU 2829 Lawrence[斜率優化dp]

HDU 2829 Lawrence[斜率優化dp]

編輯:C++入門知識

題意:大概就是給你n(1<=n<=1000)個數,要你將其分成m + 1(0<=m<n)組,要求每組數必須是連續的而且要求得到的價值最小。一組數的價值定義為該組內任意兩個數乘積之和,如果某組中僅有一個數,那麼該組數的價值為0。如:
將“4 5 1 2”分成一組得到的價值為:4*5 + 4*1 + 4*2 + 5*1 + 5*2 + 1*2 = 49;
將“4 5 1 2”分成“4 5”和“1 2”兩組得到的價值為:4*5 + 1*2 = 22;
將“4 5 1 2”分成“4”和“5 1 2”兩組得到的價值為:0 + 5*1 + 5*2 + 1*2 = 17。

分析:本題可以用動態規劃來解決,但需要斜率優化。
定義dp[i][j]表示將前j個數分成i組所得到的價值,sum[i]表示前i個數之和,w[i]表示前i個數分成一組的價值,則:
dp[i][j] = min{dp[i-1][k] + val(k+1, j)} (i-1<=k<j),val(k+1, j)為將第k+1~j個數分為一組的價值。對該式變形得:
dp[i][j] = min{dp[i-1][k] + w[j] - w[k] - sum[k]*(sum[j] - sum[k])}
          = min{dp[i-1][k] - w[k] + sum[k]*sum[k] - sum[k]*sum[j]} + w[j] (i-1<=k<j).
然後,就可以對該dp進行斜率優化。

[cpp]
#include <iostream> 
#include <cstring> 
#include <cstdio> 
#include <algorithm> 
#include <cmath> 
 
using namespace std; 
 
const int maxn = 1010; 
int n, m; 
int a[maxn], sum[maxn], w[maxn]; 
int dp[maxn][maxn]; 
int q[maxn], head, tail; 
 
int dy(int x, int j, int i) 

    return dp[x][i] - w[i] + sum[i] * sum[i] - (dp[x][j] - w[j] + sum[j] * sum[j]);  

 
int dx(int j, int i) 

    return sum[i] - sum[j]; 

 
void DP() 

    for (int i = 1; i <= n; ++i) { 
        dp[1][i] = w[i]; 
    } 
    for (int i = 2; i <= m + 1; ++i) { 
        head = tail = 0; 
        q[tail++] = i - 1; 
        for (int j = i; j <= n; ++j) { 
            while (tail - head >= 2) { 
                int p = q[head], k = q[head+1]; 
                if (dy(i - 1, p, k) < sum[j] * dx(p, k)) { 
                    head++; 
                } else { 
                    break; 
                }  
            } 
            dp[i][j] = dp[i-1][q[head]] + w[j] - w[q[head]] - sum[q[head]] * (sum[j] - sum[q[head]]); 
            while (tail - head >= 2) { 
                int p = q[tail-2], k = q[tail-1]; 
                if (dy(i - 1, p, k) * dx(k, j) >= dx(p, k) * dy(i - 1, k, j)) { 
                    tail--; 
                } else { 
                    break; 
                } 
            } 
            q[tail++] = j; 
        } 
    } 
    printf("%d\n", dp[m+1][n]);  

 www.2cto.com
int main() 

    while (scanf("%d%d", &n, &m) != EOF) { 
        if (n == 0 && m == 0) break; 
        sum[0] = 0; 
        w[0] = 0; 
        for (int i = 1; i <= n; ++i) { 
            scanf("%d", &a[i]); 
            sum[i] = sum[i-1] + a[i]; 
            w[i] = w[i-1] + sum[i-1] * a[i];  
        } 
        DP(); 
    } 
    return 0; 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved