標籤:des style os 檔案 資料 io
1282. 修路
Description
蹦蹦跳跳結束後,cxt回頭看看自己走過的路坑坑窪窪的,心中非常不爽,他表示要把這段路的路面高度修成單調上升的或者單調下降的,整條路可以看成 N段,N個整數A1,…..,An(1<=n<=2000)依次描述了每一段路的高度(0<=Ai<=1000000000)。 希望找到一個恰好含N個元素的不上升或不下降的序列B1,……,Bn,作為修過的路中每個路段的高度。
由於將每一段路墊高或挖低一個單位消耗的體力相同,於是可以表示為:
|A1-B1|+|A2-B2|+…..+|An-Bn|
請你計算一下,要修好這段道路,最少消耗多少體力。消耗的總體力不會超過2^31-1
Input Format
輸入檔案的第一行僅有一正整數N,以下的N行每行一個整數Ai,表示路面的高度。
Output Format
輸出檔案僅有一個正整數,表示如果把路修成高度不上升或不下降的最小花費
Sample Input
71324539
Sample Output
3
Hint
將第一個高度為3的路段的高度減少為2,將第二個高度為3的路段的高度增加到5,總花費為|2-3|+|5-3|=3,並且各路段的高度為一個不下降序列 1,2,2,4,5,5,9。
================================
題解本文
================================
題目分析
這是一道動態規劃題目,求最小花費使道路變成不嚴格單調遞增或不嚴格單調遞減,資料大小用int即可。
- 修改後的值肯定是原來數列裡的值。‘
- 對於結果來說,每一段都是不嚴格單調遞增或不嚴格單調遞減的數列。
- 解決方案
- 我們需要把一個數列排序(上升或下降都可以),來作為計算時的參考標準。(為什麼需要這個標準呢?因為上面提到修改後的值肯定是原來粗線過的,而最終的結果是不嚴格單調的,所以與排序後的數列比較就很方便)
- 我們需要對輸入的數列進行排序但不希望影響後來的操作,所以顯然我們得把原數列複製一遍再排序。
- 樸素的思想就是我們依次把排序後的數列的某個數當作是我們最終修正後的路面的結束值(即最大或最小),因為當作參考的數列是排序過的,所以能保證結果滿足題意,即不嚴格單調。
- 用f[i][j] 表示將原數列的前i個元素改為不嚴格單調且第i個元素改為排序後數列的第j個元素時所花費的最小的體力。用a表示原數列,b表示排序後的數列。得到狀態轉移方程f[i][j]=min{f[i-1][k]+abs(b[j]-a[i])}(1<=k<=j)
- 最後的結果就是從f[i][j]中找到最小值。
#include<iostream> #include<cmath> #include<algorithm> using namespace std; int n,ans=1<<30,a[2005],c[2005],f[2005][2005],g[2005][2005]; void init() { cin>>n; for(int i = 1; i <= n ; ++i) { cin >> a[i]; c[i]=a[i]; } sort(c+1,c+n+1); } void sol() { for(int i = 1; i <= n; ++i) { for(int j = 1; j <= n; ++j) { f[i][j] = g[i-1][j] + abs(a[i]-c[j]); if(j == 1) g[i][j] = f[i][j]; else g[i][j] = min(f[i][j],g[i][j-1]); } } for(int i = 1; i <= n; ++i) ans = min(ans ,f[n][i]); } int main() { init(); sol();//算一遍遞增 for(int i = 1; i <= n/2 ; ++i) swap(c[i],c[n-i+1]); sol();//算一遍遞減 cout<<ans; }