洛谷 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. 農(nóng)夫約翰想改造一條路,原來的路的每一段海拔是A_i,修理后是B_i,花費(fèi)|A_i – B_i|。我們要求修好的路是單調(diào)不升或者單調(diào)不降的。求最小花費(fè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.
這個(gè)題,從各種意義上講都不難,然而我沒想出來沒想出來。。。
首先一定是要離散化的,因?yàn)椴粫?huì)去修一段沒有出現(xiàn)的高度(可以證明這樣不比最優(yōu)解好)
設(shè)離散化以后的坐標(biāo),\(b[i]\)代表離散化以后排名為\(i\)的數(shù)字的值
\(dp[i][j]\)代表前\(i\)個(gè)組成的合法序列末尾元素的排名為\(j\)的最小花費(fèi)
則有轉(zhuǎn)移(單調(diào)不降)
\(dp[i][j]=min_{k=1}^j(dp[i][k]+abs(a[i]-b[j]))\)
顯然可以前綴和優(yōu)化一下子
復(fù)雜度\(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; }事實(shí)上有一種更加神奇的做法
先說操作(單調(diào)非降的):
從左到右,把當(dāng)前位置的數(shù)放進(jìn)大根堆,然后比較這個(gè)數(shù)和堆頂?shù)拇笮 ?/p>
若比堆頂大,就不管
若比堆頂小,就把堆頂拿出來變成這個(gè)數(shù),然后答案增加堆頂與這個(gè)數(shù)的差
代碼大概是這樣
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);} }為什么捏?
假設(shè)塞到第\(i\)了,前面是一個(gè)合法的遞增序列,堆頂為\(y\),當(dāng)前為\(x\)且\(x<y\)
這時(shí)候我們花掉了\(y-x\)塊錢進(jìn)行調(diào)整,考慮我們調(diào)整可以得到哪些結(jié)果
二元組\((x,x),(x+1,x+1),..(y-1,y-1),(y,y)\)都是可能的結(jié)果,雖然有的結(jié)果可能不合法,但一定存在合法的結(jié)果
我們盡可能想讓當(dāng)前的數(shù)值小,所以我們盡可能會(huì)選擇小的合法結(jié)果
這時(shí)候我們發(fā)現(xiàn),如果堆頂在后面被更新了,我們的合法結(jié)果的選擇集合就變了
如果我們直接把最小的可能不合法的結(jié)果放進(jìn)堆,那么當(dāng)比它大的元素都被砍掉后(也就是它成了堆頂),它就變得合法了
2018.8.28
轉(zhuǎn)載于:https://www.cnblogs.com/butterflydew/p/9548407.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的洛谷 P2893 [USACO08FEB]修路Making the Grade 解题报告的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CentOS 7安装Redis服务
- 下一篇: 从函数调用过程中的堆栈变化理解缓冲区溢出