HDU3507 Print Article
技術標籤:單調佇列
Problem Description
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
M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.
Input
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.
Output
A single number, meaning the mininum cost to print the article.
思路
這道題使用斜率優化,設dp[i]為前i個的最優值,那麼顯然有:
d
p
i
=
m
i
n
(
d
p
j
+
s
q
r
(
s
i
−
s
j
)
+
m
)
(
1
<
=
j
<
i
)
dp_i=min(dp_j+sqr(s_i-s_j)+m)(1<=j<i)
dpi=min(dpj+sqr(si−sj)+m)(1<=j<i)
s
i
s_i
si為字首和,然後我們考慮斜率優化。
我們首先假設在算 dp[i]時,k<j ,j點比k點優。
對上述方程進行整理
[ ( d p [ j ] + s u m [ j ] ∗ s u m [ j ] ) − ( d p [ k ] + s u m [ k ] ∗ s u m [ k ] ) ] / 2 ( s u m [ j ] − s u m [ k ] ) < = s u m [ i ] [(dp[j]+sum[j]*sum[j])-(dp[k]+sum[k]*sum[k])] / 2(sum[j]-sum[k]) <=sum[i] [(dp[j]+sum[j]∗sum[j])−(dp[k]+sum[k]∗sum[k])]/2(sum[j]−sum[k])<=sum[i].
注意整理中要考慮下正負,涉及到不等號的方向。
左邊我們令: y j = d p [ j ] + s u m [ j ] ∗ s u m [ j ] , x j = 2 ∗ s u m [ j ] y_j=dp[j]+sum[j]*sum[j] , x_j=2*sum[j] yj=dp[j]+sum[j]∗sum[j],xj=2∗sum[j]
那麼就變成了斜率表示式: ( y j − y k ) / ( x j − x k ) < = s u m [ i ] (y_j-y_k)/(x_j-x_k) <= sum[i] (yj−yk)/(xj−xk)<=sum[i];(
而且不等式右邊是遞增的。
所以我們可以看出以下兩點:我們令
g
[
k
,
j
]
=
(
y
j
−
y
k
)
/
(
x
j
−
x
k
)
g[k,j]=(y_j-y_k)/(x_j-x_k)
g[k,j]=(yj−yk)/(xj−xk)
第一:如果上面的不等式成立,那就說j比k優,而且隨著i的增大上述不等式一定是成立的,也就是對i以後算DP值時,j都比k優。那麼k就是可以淘汰的。(滿足單獨佇列特性)(單調佇列前面的出隊方法)
第二:如果 k<j<i 而且 g[k,j]>g[j,i] 那麼 j 是可以淘汰的。
如果 g[j,i]<=sum[i]就是i比j優,那麼j沒有存在的價值
如果 g[j,i]>sum[i] 那麼同樣有 g[k,j]>sum[i] 那麼 k比 j優 那麼 j 是可以淘汰的(單調佇列的末尾出隊判斷)
在程式碼實現中我們不需要用g[k,j],我們可以使用函式來代表
x
i
,
y
j
x_i,y_j
xi,yj和dp方程.
code:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<deque>
#include<cstdio>
#include<map>
using namespace std;
int f[500001],s[500001],a[500001];
int mx,t,v,w,c,b,k,x,l,r;
int n,p,q,i,m;
void read(int& x)
{
x=0;
int f=1;
char ch=getchar();
while (!isdigit(ch)) (ch=='-')&&(f=-1),ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x*=f;
}
void wr(int x)
{
(x<0)&&(x=-x,putchar('-'));
if (x>9) wr(x/10);
putchar(x%10^48);
}
int dp1(int x,int y)
{
return f[x]+m+(s[x]-s[y])*(s[x]-s[y]);
}
int f1(int x,int y)
{
return f[x]+s[x]*s[x]-f[y]-s[y]*s[y];
}
int f2(int x,int y)
{
return (s[x]-s[y]);
}
int main()
{
while (cin>>n>>m)//**讀入
{
s[0]=0;
for (i=1;i<=n;i++)
{
cin>>x;
s[i]=s[i-1]+x;
f[i]=0;
}
f[0]=0;
l=0,r=0;
for (int i=1;i<=n;i++)
{
while (l<r&&f1(a[l+1],a[l])<=2*s[i]*f2(a[l+1],a[l])) l++;
f[i]=dp1(a[l],i);
while (l<r&&f1(i,a[r])*f2(a[r],a[r-1])<=f1(a[r],a[r-1])*f2(i,a[r]))
{
r--;
}
a[++r]=i;
}
wr(f[n]);
printf("\n");
}
return 0;
}