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

Hdu 3507 Print Article (DP_斜率優化)

編輯:C++入門知識

題目大意:給定一個長度為n的序列,和一個常數m,我們可以將序列分成隨意段,每段的權值為sum(arr[i]) + C(x<=i<=y),求一種劃分方法使得整個序列的權值最小.n<=50萬。

解題思路:做完Hdu的2829,然後再看這題,一切變得如此簡單,用兩種方法解。
     狀態轉移方程為: dp[i] = min(dp[j] + (sum[i]-sum[j])^2 + m) (j < i);
     方法一:dp[i] = dp[j] + (sum[i]-sum[j])^2 + m = dp[j] + sum[i] * sum[i] + sum[j] * sum[j] - 2 * sum[i] * sum[j] + m;
     設y =  dp[j] + sum[j] * sum[j],x = sum[j],那麼原式等於:dp[i] = y + 2 * sum[i] * x + m + sum[i] * sum[i],然後套下斜率優化DP模板即可ac。
     方法二:方法二使用的優化技巧類似於四邊形不等式,用個s[i] 記錄dp[i]由前面的哪個狀態轉移過來,然後枚舉的時候只要枚舉s[i-1] 到i-1就可以了。
     第二種方法似乎比第一種要慢一些,常數比較大。

測試數據:
Input
5 5
5 9 5 7 5
1 1000
1
3 1000
1 3 5
3 0
1 3 5
1 0
100000

OutPut:
230
1001
1081
35
10000000000

C艹代碼:
[cpp]
#include <stdio.h> 
#include <string.h> 
#define MAX 510000 
#define int64 long long 
 
 
struct point{ 
 
    int64 x,y,c; 
}pot[MAX]; 
int n,m,arr[MAX]; 
int64 sum[MAX],dp[MAX]; 
int qu[MAX],head,tail; 
 
 
int CheckIt(int x,int y,int z) { 
 
    point p0 = pot[x],p1 = pot[y],p2 = pot[z]; 
    return (p0.x -p1.x) * (p0.y - p2.y) - (p0.y - p1.y) * (p0.x - p2.x) <= 0; 

int NotBest(int x,int y,int k) { 
 
    point p0 = pot[x],p1 = pot[y]; 
    return p0.y - k * p0.x > p1.y - k * p1.x; 

int64 Solve_DP() { 
 
    head = tail = 0; 
    qu[tail] = 0; 
    pot[0].x = pot[0].y = 0; 
 
 
    for (int i = 1; i <= n; ++i) { 
 
        pot[i].x = sum[i-1]; 
        pot[i].y = dp[i-1] + sum[i-1] * sum[i-1]; 
        while (head <= tail - 1 && 
                CheckIt(qu[tail-1],qu[tail],i)) tail--; 
 
 
        qu[++tail] = i; 
        while (head + 1 <= tail && 
                NotBest(qu[head],qu[head+1],2 * sum[i])) head++; 
        int k = qu[head]; 
        dp[i] = pot[k].y - 2 * sum[i] * pot[k].x + sum[i] * sum[i] + m; 
    } 
 
 
    return dp[n]; 

int64 Solve_DP2() { 
 
    for (int64 mmin,i = 1; i <= n; ++i) { 
 
        mmin = -1; 
        for (int j = qu[i-1]; j < i; ++j) 
            if (mmin == -1 || 
                    dp[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]) < mmin) { 
 
                mmin = dp[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]); 
                qu[i] = j; 
            } 
 
 
        dp[i] = mmin + m; 
    } 
    return dp[n]; 

 
 
int main() 

    int i,j,k; www.2cto.com
 
 
    while (scanf("%d%d",&n,&m) != EOF) { 
 
        for (i = 1; i <= n; ++i) 
            scanf("%d",&arr[i]),sum[i] = arr[i] + sum[i-1]; 
 
 
        int64 ans = Solve_DP2(); 
        printf("%lld\n",ans); 
    } 

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