洛谷 P2893 [USACO08FEB]修路Making the Grade 解题报告
P2893 [USACO08FEB]修路Making the Grade
題目描述
A straight dirt road connects two fields on FJ's farm, but it changes elevation more than FJ would like. His cows do not mind climbing up or down a single slope, but they are not fond of an alternating succession of hills and valleys. FJ would like to add and remove dirt from the road so that it becomes one monotonic slope (either sloping up or down).
You are given N integers A1, ... , AN (1 ≤ N ≤ 2,000) describing the elevation (0 ≤ Ai ≤ 1,000,000,000) at each of N equally-spaced positions along the road, starting at the first field and ending at the other. FJ would like to adjust these elevations to a new sequence B1, . ... , BN that is either nonincreasing or nondecreasing. Since it costs the same amount of money to add or remove dirt at any position along the road, the total cost of modifying the road is
|A1 - B1| + |A2 - B2| + ... + |AN - BN |Please compute the minimum cost of grading his road so it becomes a continuous slope. FJ happily informs you that signed 32-bit integers can certainly be used to compute the answer. 農夫約翰想改造一條路,原來的路的每一段海拔是A_i,修理后是B_i,花費|A_i – B_i|。我們要求修好的路是單調不升或者單調不降的。求最小花費。
輸入輸出格式
輸入格式:
Line 1: A single integer: N
Lines 2..N+1: Line i+1 contains a single integer elevation: Ai
輸出格式:
- Line 1: A single integer that is the minimum cost for FJ to grade his dirt road so it becomes nonincreasing or nondecreasing in elevation.
這個題,從各種意義上講都不難,然而我沒想出來沒想出來。。。
首先一定是要離散化的,因為不會去修一段沒有出現的高度(可以證明這樣不比最優解好)
設離散化以后的坐標,\(b[i]\)代表離散化以后排名為\(i\)的數字的值
\(dp[i][j]\)代表前\(i\)個組成的合法序列末尾元素的排名為\(j\)的最小花費
則有轉移(單調不降)
\(dp[i][j]=min_{k=1}^j(dp[i][k]+abs(a[i]-b[j]))\)
顯然可以前綴和優化一下子
復雜度\(O(N^2)\)
Code:
#include <cstdio> #include <algorithm> #include <cstring> const int N=2e3+10; int dp[N][N],g[N][N],n,a[N],b[N]; int min(int x,int y){return x<y?x:y;} int max(int x,int y){return x>y?x:y;} int abs(int x){return x>0?x:-x;} int main() {scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",a+i),b[i]=a[i];std::sort(b+1,b+1+n);memset(g,0x3f,sizeof(g));memset(g[0],0,sizeof(g[0]));for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){dp[i][j]=g[i-1][j]+abs(a[i]-b[j]);g[i][j]=min(g[i][j-1],dp[i][j]);}int ans=g[n][n];memset(g,0x3f,sizeof(g));memset(g[0],0,sizeof(g[0]));for(int i=1;i<=n;i++)for(int j=n;j;j--){dp[i][j]=g[i-1][j]+abs(a[i]-b[j]);g[i][j]=min(g[i][j+1],dp[i][j]);}ans=min(ans,g[n][n]);printf("%d\n",ans);return 0; }事實上有一種更加神奇的做法
先說操作(單調非降的):
從左到右,把當前位置的數放進大根堆,然后比較這個數和堆頂的大小。
若比堆頂大,就不管
若比堆頂小,就把堆頂拿出來變成這個數,然后答案增加堆頂與這個數的差
代碼大概是這樣
for(int i=1;i<=n;i++) {scanf("%d",&a);q.push(a);if(a<q.top()){ans+=q.top()-a;q.pop();q.push(a);} }為什么捏?
假設塞到第\(i\)了,前面是一個合法的遞增序列,堆頂為\(y\),當前為\(x\)且\(x<y\)
這時候我們花掉了\(y-x\)塊錢進行調整,考慮我們調整可以得到哪些結果
二元組\((x,x),(x+1,x+1),..(y-1,y-1),(y,y)\)都是可能的結果,雖然有的結果可能不合法,但一定存在合法的結果
我們盡可能想讓當前的數值小,所以我們盡可能會選擇小的合法結果
這時候我們發現,如果堆頂在后面被更新了,我們的合法結果的選擇集合就變了
如果我們直接把最小的可能不合法的結果放進堆,那么當比它大的元素都被砍掉后(也就是它成了堆頂),它就變得合法了
2018.8.28
轉載于:https://www.cnblogs.com/butterflydew/p/9548407.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的洛谷 P2893 [USACO08FEB]修路Making the Grade 解题报告的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CentOS 7安装Redis服务
- 下一篇: 从函数调用过程中的堆栈变化理解缓冲区溢出