1. 程式人生 > >[四連測(一)]電話線路

[四連測(一)]電話線路

題目描述

最近,約翰的奶牛們越來越不滿足於牛棚裡一塌糊塗的電話服務,於是,她們要求
約翰把那些老舊的電話線換成效能更好的新電話線。新的電話線架設在己有的n根
電話線杆上,第i根電話線的高度為hi, ( 1 <= hi<= 100)。電話線總是從一根電話
線杆的頂端被弓}到相鄰的那根的頂端,如果這兩根電話線杆的高度hi和hj不同,那
麼約翰就必須支付c * |hi - hj|的費用,當然,你不能行動電話線杆,只能按照原有
的順序在相鄰杆間架設電話線。
加高某些電話線杆能減少架設電話線的總費用,儘管這項工作也需要支付一定的
費用。更準確的說,如果他把一根電話線杆加高x米的話,他需要付出x^2費用。
請你幫約翰計算一下,如果合理的進行這兩項工作,他最少要在這個電話線改造
工程中花多少錢

輸入

第一行輸入兩個數n和c, 含義如上
接下來n行,每行一個整數hi

輸出

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

樣例輸入

5 2
2
3
5
1
4

樣例輸出

15

資料範圍

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

解題思路

首先,確定題型:DP(感覺好難啊,看到DP就怕了)既然是DP,那該怎麼定義呢?首先,第一維i肯定是第幾根電線杆。這是毋庸置疑的,那麼第二維呢?這裡有幾個選擇。

(一)第二維放第i根電線子改變後的高度。

(二)第二維放第i根電線子所增加的高度。

此題想來只能用二維DP,這樣的資料範圍三維是絕對會爆的,於是我們就不考慮三維這樣的情況。(然而考試時候深陷其中無法自拔,簡單的二維都想不到了。。。。)

由於知道每根電線杆的原高度,我們用j表示新的高度(即第二維放第i根電線杆的高度)。那麼便可以求出從a[i]到j所需要的費用,這樣是比較好算的,並且儲存了第i根電線杆j高時的最優狀態,後面算高度差時也比較好用。

這樣DP[i][j]就表示第i杆電線杆高度為j時所花的最小費用。

狀態轉移方程:dp[i][j]=min\left \{dp[i-1][k]+c*\left |j-k \right | \right \}+\left ( j - a[i] \right ) ^2

然而。。。其中有三個變數i,j,k。還不是用三重循壞不也爆了?這個時候,就要開始進行優化了。我們從狀態轉移方程中入手,min後面的那一塊是一個定值,我們可以不用管。|j-k|

一看就要分類討論,於是分成了j <= k j > k。等於無論分在哪邊效果一樣的

於是,c * |j - k|就可以化了於是又得到兩種不同情況的狀態轉移方程

dp[i][j] = min\left \{dp[i - 1][k] + c * k \right \} - c * j + (j - a[i])^{2}(k>=j)

dp[i][j] = min\left \{dp[i - 1][k] - c * k \right \} + c * j + (j - a[i])^{2}(k<j)

我們可以知道j其實在列舉更值得時候是一個定值,我們於是就可以將它提出來。做到簡化的目的,你也許會想,這樣依舊還是要爆。這時候我們最好就是要同時將j,k都枚舉了。這樣才能夠不超時。事實上這是可以做到的。我們通過優化列舉j就可以達到效果

進行預處理,第一個電線杆達到的j高度的花費就是(j - a[i])^2

首先就要確定列舉的方向,可以清楚的是,這兩個分類討論的情況是需要分開進行處理的。那麼第一個(k>=j,意思是如果k只能取j到100的值)就需要倒著進行列舉。因為這樣就可儲存中間的min,同理,第二個(k<j)正著列舉。

我們用兩個變數,在列舉過程中,儲存最小值,更新DP值。更新完之後也儲存這一個情況的最小值。可能有點抽象。具體看一下程式碼

for (int i = 2;i <= n; i++){//列舉每一個電線杆,第一個電線杆進行了預處理
        int minn = inf ,minn1 = inf;
        for (int j = 1 ;j <= a[i] ;j ++ ){//這一段是k<j的情況,由於j是不可能取到小於a[i]的值,因此這裡就是求一個最小值
            minn = min((dp[i - 1][j] - c * j) , minn);
        }
        for (int j = a[i] ; j <= 100 ; j ++ ){//k<j的情況,在這裡我們就需要更新DP的值
            minn = min(minn,dp[i - 1][j] - c * j);
            dp[i][j] = min(dp[i][j],minn + c * j + (j - a[i]) * (j - a[i]));
        }
        for (int j = 100;j >= a[i]; j -- ){//k > j的情況,在這裡我們需用另一個變數,儲存最小值。並且更新DP的值。
            minn1 = min(minn1, (dp[i - 1][j] + c * j));
            dp[i][j] = min(dp[i][j],minn1 - c * j + (j - a[i]) * (j - a[i]));
        }
    }

完整程式碼就不放了,其實除了輸入輸出與預處理,就這一部分程式碼了。

總結

DP總覺得還是比較難想啊。考試時候根本不好弄DP,縱使我想出了DP,也還是想不到優化的方法。此題的關鍵點可以說不在DP,而在優化。對於DP列舉j順序的改變可以說很難想了。但實現卻是不難,想到一種簡單的狀態轉移方程就顯得尤其重要,回想了一下,在我想狀態轉移方程的時候,基本上都轉移不了,根本無法表示,更不用說做題了,這就需要我去抓住題目中的本質資訊就比如說這道題,題目中編號肯定是有的,同時高度又決定了值,所以我們最好將高度定下來,於是高度變成了j來進行列舉。而優化的步驟則抓住了不同DP狀態的條件,改變順序。有些題,拿到題還是要判斷演算法,用那樣的一種思路去進行計算。