hdu 3507 Print Article(斜率優化dp)

題意:把一堆數分成幾堆,每堆的代價由這個式子:算出。求總代價最小。

做法:另dp[i]是分到第i個總代價最小,那麼轉移方程,dp[i] = dp[j]+(sum[i]-sum[j])^2+M。可以發現遞推複雜度是o(n^2)。

我們變形一下這個式子:dp[i]-M-sum[i]^2 = dp[j]+sum[j]^2-2sum[i]*sum[j],等式左邊都是不變量,即要求等式右邊的最小值對應的j,我們另y = dp[j]+sum[j]^2,x = sum[j],對於不同的j來說,x和y都是確定的,再另k = 2*sum[i],k對於當前要求的i來說也是一個定值,那麼我們的目標就是要求y + kx最小,假設對於2個不同的位置來說,(x1,y1),(x2,y2),如果前者要比後者小,那麼就是y1-k*x1 < y2-k*x2,即

(y1-y2)/(x1-x2) < k,而k是單調遞增的,所以我們可以維護一個單調隊列來遞推最優解,隊列裏的點都滿足斜率變大,所以說是一個下凸的,如果新加進來的點與前一個點不滿足下凸,那麼前一個點必定可以刪除,因爲如果斜率大於等於k,再前面一個點比前一點優,如果小於k,那麼新加入的這個點就比前一點優了。對於當前的k來說,因爲隊列裏的斜率是單調遞增的,通過前面的式子可以得知,如果斜率小於當前k,那麼後一個點就更優,我們就可以移動head的指針,直至斜率大於當前k,因爲大於了意味着後一個點不比當前點優。

AC代碼:

#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<string.h>
#include<string>
#include<sstream>
#include<bitset>
using namespace std;
#define ll long long
#define ull unsigned long long
#define eps 1e-8
#define NMAX 201000
#define MOD 1000000007
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI acos(-1)
template<class T>
inline void scan_d(T &ret)
{
    char c;
    int flag = 0;
    ret=0;
    while(((c=getchar())<'0'||c>'9')&&c!='-');
    if(c == '-')
    {
        flag = 1;
        c = getchar();
    }
    while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
    if(flag) ret = -ret;
}
const int maxn = 500000+10;
int sum[maxn],q[maxn],dp[maxn];

inline int gety(int a)
{
    return dp[a]+sum[a]*sum[a];
}

inline int getx(int a)
{
    return sum[a];
}

int suby(int a, int b)
{
    return gety(a)-gety(b);
}

int subx(int a, int b)
{
    return getx(a)-getx(b);
}

int main()
{
#ifdef GLQ
    freopen("input.txt","r",stdin);
//    freopen("o.txt","w",stdout);
#endif
    int n,m;
    sum[0] = 0;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i = 1; i <= n; i++)
        {
            int t;
            scanf("%d",&t);
            sum[i] = sum[i-1]+t;
        }
        dp[0] = 0;
        int head = 0,rear = 1;
        q[0] = 0;
        for(int i = 1; i <= n; i++)
        {
            while(rear-head > 1 && suby(q[head+1],q[head]) <= 2*sum[i]*subx(q[head+1],q[head])) head++;
            dp[i] = gety(q[head])-2*sum[i]*getx(q[head])+sum[i]*sum[i]+m;
            while(rear-head > 1 && suby(i,q[rear-1])*subx(q[rear-1],q[rear-2]) <= suby(q[rear-1],q[rear-2])*subx(i,q[rear-1]))
                rear--;
            q[rear++] = i;
        }
        printf("%d\n",dp[n]);
    }
    return 0;
}


發佈了187 篇原創文章 · 獲贊 9 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章