1. 程式人生 > 實用技巧 >HDU3507-Print Article

HDU3507-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

M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.

輸入輸出格式

輸入格式

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.

題意

\(N\)個數,將序列分為若干段,代價為每一段的和的平方加常數\(M\),求最小代價。

思路

這是一道比較基礎的斜率優化DP的題
首先,我們可以推出最基礎的DP狀態轉移
我們定義\(dp_i\)為前i個數的最小代價,則
\(dp_i=\min(dp_j+(sum_i-sum_j)^2+M)\) (\(sum_i\)表示序列前\(i\)個數的字首和)
顯然,這個狀態轉移方程的複雜度為\(O(n^2)\)
但這個題裡\(n≤500000\)顯然是會超時的,那麼我們可以使用斜率優化DP降低複雜度

我們假設在求解\(dp_i\)時,有兩個決策點\(k<j<i\),且\(j\)更優。
那麼,
\(dp_j+( sum_i-sum_j )^2 < dp_k+( sum[i]-sum[k] )^2\)

\(\Longrightarrow\) \(dp_j+sum_i^2-2sum_i*sum_j+sum_j^2 < dp[k]+sum_i^2-2sum_i*sum_k+sum_k^2\)

\(\Longrightarrow\) \(( dp_j+sum_j^2 )-( dp_k+sum_k^2 ) < 2sum_i*( sum_j-sum_k )\)

\(\Longrightarrow\) \(\frac{( dp_j+sum_j^2 )-( dp_k+sum_k^2 )}{2( sum_j-sum_k )} < sum[i]\)

\(dp_j+sum_j^2\)\(y_j\)\(dp_k+sum_k^2\)為y_k,\(sum_j\)\(x_j\)\(sum_k\)\(x_k\)

\(\frac{y_j-y_k}{2*( x_j-x_k )}<sum_i\)
\(g_{kj}=\frac{y_j-y_k}{x_j-x_k}\),我們可以看出以下兩點:
1、如果不等式成立,那就說明\(j\)\(k\)優,而且隨著\(i\)的增大不等式一定是成立的,也就是說之後對\(i\)算DP時\(j\)都比\(k\)優,所以\(k\)可以淘汰。
2、如果\(k<j<i\),而且\(g_{kj}>g_{ji}\)那麼\(j\)就可以淘汰。假設\(g_{ji}<sum_i\)就是\(i\)\(j\)優,即\(j\)可淘汰。反之如果\(g_{ji}>sum_i\)那麼同樣有g_{kj}>sum_i,那麼\(k\)\(j\)優,\(j\)也可淘汰
所以我們相當於在維護一個下凸的圖形,斜率不斷變大,我們就可以通過一個佇列進行維護。

程式碼

#include<bits/stdc++.h> 
#define int long long
using namespace std;
const int maxn=5e5+5;
int dp[maxn],sum[maxn],q[maxn],n,m,a[maxn];
int calc_up(int j,int k) {
	return dp[j]+sum[j]*sum[j]-(dp[k]+sum[k]*sum[k]);//分子 
}
int calc_down(int j,int k) {
	return 2*(sum[j]-sum[k]);//分母
}
signed main() {
	while(cin>>n>>m) {
		sum[0]=dp[0]=0;
		for (int i=1;i<=n;i++) {
			cin>>a[i];
			sum[i]=sum[i-1]+a[i];//字首和
		}
		int l=1,r=0;//初始化
		q[++r]=0;
		for (int i=1;i<=n;i++) {
			while(l+1<=r && calc_up(q[l+1],q[l])<=sum[i]*calc_down(q[l+1],q[l])) l++;
			int j=q[l];//最優的決策點
			dp[i]=dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);
			while(l+1<=r && calc_up(i,q[r])*calc_down(q[r],q[r-1])<=calc_up(q[r],q[r-1])*calc_down(i,q[r])) r--;
			q[++r]=i;
		}
		cout<<dp[n]<<endl; 
	}
	return 0;
}