1. 程式人生 > >HDU 4258 斜率優化dp

HDU 4258 斜率優化dp

pan num several others iostream miss key art contain

Covered Walkway

Time Limit: 30000/10000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1496 Accepted Submission(s): 602


Problem Description Your university wants to build a new walkway, and they want at least part of it to be covered. There are certain points which must be covered. It doesn’t matter if other points along the walkway are covered or not.
The building contractor has an interesting pricing scheme. To cover the walkway from a point at x to a point at y, they will charge c+(x-y)2, where c is a constant. Note that it is possible for x=y. If so, then the contractor would simply charge c.
Given the points along the walkway and the constant c
, what is the minimum cost to cover the walkway?

Input There will be several test cases in the input. Each test case will begin with a line with two integers, n (1≤n≤1,000,000) and c (1≤c≤109), where n is the number of points which must be covered, and c is the contractor’s constant. Each of the following n
lines will contain a single integer, representing a point along the walkway that must be covered. The points will be in order, from smallest to largest. All of the points will be in the range from 1 to 109, inclusive. The input will end with a line with two 0s.

Output For each test case, output a single integer, representing the minimum cost to cover all of the specified points. Output each integer on its own line, with no spaces, and do not print any blank lines between answers. All possible inputs yield answers which will fit in a signed 64-bit integer.

Sample Input 10 5000 1 23 45 67 101 124 560 789 990 1019 0 0

Sample Output 30726

Source The University of Chicago Invitational Programming Contest 2012

題意:

有n個點需要被覆蓋,覆蓋第j到第i之間的點的花費是c+(x[i]-x[j])^2,問把所有的點都覆蓋的最小花費。

輸入n,c

輸入n個點x[1...n]

當輸入0 0時結束

代碼:

//有狀態轉移方程dp[i]=min(dp[j]+C+(a[i]-a[j+1])*(a[i]-a[j+1])),數據是1e6的兩重循環必然不行
//設k<j<i,當到達i點時如果從j點轉移到i比從k點轉移到i更優則有:dp[j]+C+(a[i]-a[j+1])^2<dp[k]+C+(a[i]-a[k+1])^2
//展開得:(dp[j]-dp[k]+a[j+1]^2-a[k+1]^2)/2*(a[j+1]-a[k+1])<a[i].其中a[i]常量(實現時是一重循環枚舉i點),
//我們設yj=dp[j]+a[j+1]^2,xj=a[j+1] =>(yj-yk)/2*(xj-xk)<a[i].左邊是計算斜率的式子。我們用一個單調隊列
//來存儲能夠轉移到i點狀態的點並且隊頭是轉移到i點狀態的最優的解,每次要保持隊頭是最優解就要根據
//(yj-yk)/2*(xj-xk)<a[i]用隊頭去和隊列中第二個去比較(如果隊頭不優於第二個就要刪去隊頭元素)。
//設g[i,j]表示直線j-i的斜率,如果有g[k,j]>g[i,j]那麽j點永遠不可能是i的最優解,因為:
//我們假設g[i,j]<a[i],那麽就是說i點要比j點優,排除j點。如果g[i,j]>=a[i],那麽j點此時是比i點要更優,
//但是同時g[j,k]>g[i,j]>sum[i]。這說明還有k點會比j點更優,同樣排除j點。排除多余的點,這便是一種優化!
//其實就是維護一個斜率遞增的(下凸上凹)的圖形。因此要把i點加入隊列之前先判斷是否能夠維護斜率遞增如果
//不能就把隊列最後一個元素刪掉直到是斜率遞增的然後加入i點。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1000009;
int n,m,que[maxn];
ll dp[maxn],a[maxn];
ll getdp(int i,int j){
    return dp[j]+m+(a[i]-a[j+1])*(a[i]-a[j+1]);
}
ll getup(int j,int k){
    return dp[j]-dp[k]+a[j+1]*a[j+1]-a[k+1]*a[k+1];
}
ll getlow(int j,int k){
    return 2*(a[j+1]-a[k+1]);
}
int main()
{
    while(scanf("%d%d",&n,&m)&&(n+m)){
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        int head=0,tail=0;
        dp[0]=0;
        que[tail++]=0;
        for(int i=1;i<=n;i++){
            while(head+1<tail&&getup(que[head+1],que[head])<=a[i]*getlow(que[head+1],que[head]))
                head++;
            dp[i]=getdp(i,que[head]);
            while(head+1<tail&&getup(que[tail-1],que[tail-2])*getlow(i,que[tail-1])>=getup(i,que[tail-1])*getlow(que[tail-1],que[tail-2]))
                tail--;
            que[tail++]=i;
        }
        printf("%lld\n",dp[n]);
    }
    return 0;
}

HDU 4258 斜率優化dp