動態規劃 斜率優化

 hdu4258

/**
 * Covered Walkway
 * @author vanb
 *
 * This problem would be pretty easy, if it weren't for the size of the data sets.
 * If the data sets were small, a simple DP solution would suffice:
 *
 * Let best[i] be the best you can do covering A[0]..A[i].
 * Then best[i] = MIN{best[j] + C + (A[i]-A[j+1])^2} for j<i
 * and best[n-1] is your solution.
 *
 * The simple solution would be to implement two loops:
 * for( int i=0; i<n; i++ ) to compute each best[i],
 * and for( int j=0; j<i; j++ ) to find that minimum.
 * But, that would be O(n^2), and when n can be as large as a million,
 * that's too much.
 *
 * This solution will use that basic concept, but we'll find a more efficient way
 * of finding that minimum.
 *
 * Suppose we're at A[i], and we're trying to choose whether it's best to break
 * at A[j] or A[k] (j<k<i). Note that since the input is guaranteed to be in
 * increasing order, A[j]<A[k]<A[i]. So, we've got to compare:
 * best[j] + C + (A[i]-A[j+1])^2 to best[k] + C + (A[i]-A[k+1])^2
 * best[j] + C + A[i]^2 - 2*A[i]*A[j+1] + A[j+1]^2 to best[k] + C + A[i]^2 - 2*A[i]*A[k+1] + A[k+1]^2
 *
 * Since C and A[i] will be the same for any j & k, then we're comparing
 * best[j] - 2*A[i]*A[j+1] + A[j+1]^2 to best[k] - 2*A[i]*A[k+1] + A[k+1]^2.
 *
 * If we look at this as a function of A[i] (in fact, let x=A[i]), it looks like this:
 * -2*a[j+1]*x + (best[j]+a[j+1]^2) compared to -2*a[k+1]*x + (best[k]+a[k+1]^2)
 *
 * So, for any A[i], if we want to know if splitting between A[j] and A[j+1] is better
 * than splitting between A[k] and A[k+1], then all we have to do is plug A[i] in
 * for x in this linear equation, and see which is smaller (since smaller is better).
 *
 * But, since these are linear, we can take some shortcuts. Think of them as lines,
 * in y=mx+b form, with m(j)=-2*A[j+1], and b(j)=(best[j]+a[j+1]^2). Look at that slope (m(j)).
 * As j increases, so does A[j+1], and so that slope becomes more intensely negative.
 * That means that, if j<k, once k becomes a better breakpoint, j will never be better again.
 * That is, if j<k and m(k)*x+b(k) < m(j)*x+b(j) for some x,
 * then m(k)*w+b(k) < m(j)*w+b(j) for all w>x.
 * Once k becomes better, we never have to look at j again.
 *
 * We'll maintain a linked list of breakpoints, and remember their m's and b's.
 * We'll walk forward through this list, and never have to go back. Any new
 * breakpoint we add will have the most negative slope, so it will need to be
 * added to the end of the list. But, it may shunt off some other breakpoints.
 * So, we'll add it to the end of the list and remove any breakpoints that it
 * shunts off. If we do that, then we won't have to go through the whole list to find
 * the minimum. Every breakpoint will either get walked over once, or shunted off once.
 * Either way, we've reduced that process of finding the minimum to O(n) prorated
 * over the entire algorithm, making the whole thing O(n).
 *  
 */

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;
typedef long long ll;

typedef pair<ll, ll> pll;

#define fst first
#define scd second
const int maxn=1000000+123;
ll w[maxn];
ll dp[maxn];
pll queue[maxn];

ll calc(pll v, ll x)
{
    return x*v.fst+v.scd;
}
/// dp[i]=min(dp[j-1]+(x[i]-x[j])^2)
/// ==> dp[i]=x[i]^2+min(dp[j-1]-2*x[i]*x[j]+x[j]^2);
/// made b=dp[j-1]+x[j]*x[j];

bool good(pll x, pll x1, int y)
{
    return calc(x, w[y])>calc(x1, w[y]);
}

/// 在x1比x更優前, y已經比x1更優, 此時x1直接從尾部彈出隊列。
/// 由於直線的斜率是單調遞減的,所以當2條直線相交後, 後一條一定比前一條更優,求出交點
bool bad(pll x, pll x1, pll y)
{
    //cout << (dp[x1-1]-dp[x-1])/(w[x]-w[x1]) << " " << (dp[x1-1]-dp[y-1])/(w[y]-w[x1]) << endl;
    //return (dp[x1-1]+w[x1]*w[x1]-dp[x-1]-w[x]*w[x])/(w[x]-w[x1])<(dp[x1-1]+w[x1]*w[x1]-dp[y-1]-w[y]*w[y])/(w[y]-w[x1]);
    return (x1.scd-x.scd)/(x.fst-x1.fst)>(x1.scd-y.scd)/(y.fst-x1.fst);
}

int main ()
{
    ll c;
    int n;
    while (cin >> n >> c && (n || c))
    {
        for (int i=1; i<=n; ++i)
            scanf("%I64d", w+i);
        dp[0]=0; dp[1]=c;
        int head=0, rear=-1;
        queue[++rear]=make_pair(-2*w[1], w[1]*w[1]);
        for (int i=2; i<=n; ++i)
        {
            pll tt=make_pair(-2*w[i], dp[i-1]+w[i]*w[i]);
            while (head<rear && bad(queue[rear], queue[rear-1], tt))rear--;
            queue[++rear]=tt;
            while (head<rear && good(queue[head], queue[head+1], i))head++;
            dp[i]=c+w[i]*w[i]+calc(queue[head], w[i]);
//            for (int j=head+1; j<=rear; ++j)
//            {
//                ll tmp=dp[queue[j]-1]+calc(queue[j], i)+c;
//                if(dp[i]>tmp){dp[i]=tmp; head=j;}
//            }
//            printf("r=%d t=%d  dp[%d]=%I64d\n", head, rear,  i, dp[i]);
        }
        cout << dp[n] << endl;
    }
    return 0;
}
/*
10 1000000000
1000
23000
450000
6700000
10100000
12400000
56000000
78900000
99000000
101900000
6 4
1 2 5 6 7 8
10 5000
1
23
45
67
101
124
560
789
990
1019
0 0
*/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章