1. 程式人生 > >Telephone Wire [USACO07NOV] 洛谷P2885 [單調佇列優化dp]

Telephone Wire [USACO07NOV] 洛谷P2885 [單調佇列優化dp]

文章目錄

Description

最近,約翰的奶牛們越來越不滿足於牛棚裡一塌糊塗的電話服務,於是,她們要求約翰把那些老舊的電話線換成效能更好的新電話線。

新的電話線架設在己有的n根電話線杆上,第i根電話線的高度為hi, ( 1 <= hi<= 100)。電話線總是從一根電話線杆的頂端到相鄰的那根的頂端,如果這兩根電話線杆的高度hi和hj不同,那麼約翰就必須支付c * |hi - hj|的費用,當然,你不能行動電話線杆,只能按照原有的順序在相鄰杆間架設電話線。

加高某些電話線杆能減少架設電話線的總費用盡管這項工作也需要支付一定的費用。更準確的說,如果他把一根電話線杆加高x米的話,他需要付出x^2費用。

請你幫約翰計算一下,如果合理的進行這兩項工作,他最少要在這個電話線改造
工程中花多少錢。

洛谷P2885

Input

第一行輸入兩個數n和c, 含義如上
接下來n行,每行一個整數hi
【資料範圍】

N <= 100000, C <= 100, Hi < 100,答案不超過maxlongint。

Output

輸出約翰完成電話線改造工程需要花費的最小費用

Sample Input

5 2
2
3
5
1
4

Sample Output

15

分析

首先考慮一般的dp狀態轉移
由於花費與高度相關 所以要有第二維表示高度

定義dp[i][j]為前i根電線杆 且第i根高度為j時的最小花費
此時的狀態轉移方程:
dp[i][j]=min(dp[i-1][k]+abs(j-k)*c)+(j-h[i]) * (j-h[i])
k是需要列舉的上一根電線杆的高度

那這樣就需要列舉三維 肯定過不了這麼大的資料

分析狀態轉移方程,我們先把絕對值拆開

j>=k時
dp[i][j]
=min(dp[i-1][k]+(j-k) * c)+(j-h[i]) * (j-h[i])
=min(dp[i-1][k]-k * c)+ (j-h[i]) * (j-h[i])+j*c

min裡面的部分就只與k有關 那麼就可以用單調佇列優化

同理
j<k時
dp[i][j]
=min(dp[i-1][k]+(k-j) * c)+ (j-h[i]) * (j-h[i])
=min(dp[i-1][k]+k * c)+(j-h[i]) * (j-h[i])- j * c

分成兩種情況分別更新即可
事實上 採用合理的遍歷順序 我們根本不需要用單調佇列來維護 只需要一個變數就可以

程式碼

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 100005
#define MAXH 100 
#define INF 0x3f3f3f3f
int n,c,h[MAXN],dp[MAXN][MAXH+5];
int main()
{
	scanf("%d %d",&n,&c);
	for(int i=1;i<=n;i++)
		scanf("%d",&h[i]);
	memset(dp,0x3f,sizeof(dp));
	int minx=INF;
	for(int j=h[1];j<=MAXH;j++)
		dp[1][j]=(j-h[1])*(j-h[1]);
	for(int i=2;i<=n;i++)
	{
		minx=INF;
		for(int k=1;k<h[i];k++)
			minx=min(minx,dp[i-1][k]-k*c);
		for(int j=h[i];j<=100;j++)
		{
			minx=min(minx,dp[i-1][j]-j*c);
			dp[i][j]=min(dp[i][j],minx+(j-h[i])*(j-h[i])+j*c);
		}
		minx=INF;
		for(int j=100;j>=h[i];j--)
		{
			minx=min(minx,dp[i-1][j]+c*j);
			dp[i][j]=min(dp[i][j],minx+(j-h[i])*(j-h[i])-j*c);
		}
	}
	int ans=INF;
	for(int i=h[n];i<=100;i++)
		ans=min(ans,dp[n][i]);
	printf("%d\n",ans);
	return 0;
}