【動態規劃15】hdu3057 Print Article(斜率優化入門)

題目描述

Zero has an old printer that doesn’t work well sometimes. As it is antique, he still like to use it to print articles. But it is too old to work for a long time and it will certainly wear and tear, so Zero use a cost to evaluate this degree.
One day Zero want to print an article which has N words, and each word i has a cost Ci to be printed. Also, Zero know that print k words in one line will cost(ni=1Ci)2 +M
M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.

就是一個數列a[N],每次可以從中選出一個區間輸出,代價爲區間和的平方+M,求全部輸出的最小代價。

輸入輸出格式

There are many test cases. For each test case, There are two numbers N and M in the first line (0 ≤ n ≤ 500000, 0 ≤ M ≤ 1000). Then, there are N numbers in the next 2 to N + 1 lines. Input are terminated by EOF.
A single number, meaning the mininum cost to print the article.

直接暴力想轉移O(n^2)那就很顯然了,設f[i]輸出爲前i位的最少代價。
先維護a[N]的前綴和pre,然後就很顯然了。
f[i]=min(f[j]+(pre[i]pre[j])2+M)(i>j)
但是這道題的數據範圍中n<=500000,O(n^2)的時間複雜度那就炸到天上去了。
k<j<i
我們先設i從j轉移比從k轉移的情況更優,則有
f[j]+(pre[i]pre[j])2<f[k]+(pre[i]pre[k])2 (消掉M)
f[j]+pre[i]2+pre[j]22pre[i]pre[j]<f[k]+pre[i]2+pre[k]22pre[i]pre[k]
f[j]+pre[j]2f[k]pre[k]2<2pre[i](pre[j]pre[k])
pre[i]>(f[j]+pre[j]2)(f[k]+pre[k]2)2pre[j]2pre[k]

這時候我們發現這個式子當中j與k的組成元素是對應的。

xj=2*pre[j]
xk=2*pre[k]
yj=pre[j]2 +f[j]
yk=pre[k]2 +f[k]
原式變爲pre[i]>yjykxjxk
而此時,式子右邊的值可以看成兩個點(xj,yj),(xk,yk)組成的直線的斜率的值。記這種斜率爲K[j][k]。
且由於K[j][k]<pre[i]<pre[i+p](p>=1) ,故若k優於j,那麼k一定恆優於j
現在我們再來考慮三個點的情況,設j<k<l
K[j][k]<pre[i] 等價於決策k優於決策j
K[j][k]>K[k][[l]
{
  ①若K[k,l]<pre[i] ,那麼顯然l要優於k
  ②若K[k,l]>pre[i] ,則K[j][k]>K[k][l]>pre[i] ,那麼決策j是優於k的
  綜上,此時k一定不會是最優的。
}
那麼只要在每次轉移的時候維護一個隊列,每次從隊頭開始轉移就顯然了。
(別忘了開long long)

#include<bits/stdc++.h>
#define fer(i,j,n) for(int i=j;i<=n;i++)
#define far(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
const int maxn=500010;
const int INF=1e9+7;
using namespace std;
/*----------------------------------------------------------------------------*/
inline ll read()
{
    char ls;ll x=0,sng=1;
    for(;ls<'0'||ls>'9';ls=getchar())if(ls=='-')sng=-1;
    for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
    return x*sng;
}
/*----------------------------------------------------------------------------*/
ll n,m;
ll pre[maxn],f[maxn];
ll q[maxn];
ll calcK(int j,int k)
{
    if(pre[j]==pre[k])
        if(f[j]>f[k])return -1;
        else return INF;
    return (f[j]+pre[j]*pre[j]-f[k]-pre[k]*pre[k])/(2*pre[j]-2*pre[k]);
}
int main()
{
    while(scanf("%lld%lld",&n,&m)!=EOF)
    {
        fer(i,1,n)
        pre[i]=read()+pre[i-1];
        int h=0,t=0;
        fer(i,1,n)
        {
            while(h<t&&calcK(q[h],q[h+1])<pre[i])h++;
            f[i]=f[q[h]]+(pre[i]-pre[q[h]])*(pre[i]-pre[q[h]])+m;
            while(h<t&&calcK(q[t-1],q[t])>calcK(q[t],i))t--;
            q[++t]=i;
        }
        cout<<f[n]<<endl;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章