路面修整
【问题描述】
大老板Mr. Yao打算好好修一下公司门口的那条凹凸不平的路。按照Mr. Yao的设想,修好后的路面高度应当单调上升或单调下降,也就是说,高度上升与高度下降的路段不能同时出现在修好的路中。
整条路被分成了N段,N个整数A_1, ... , A_N (1 <= N <= 2,000)依次描述了每一段路的高度(0 <= A_i <= 1,000,000,000)。Mr. Yao希望找到一个恰好含N个元素的不上升或不下降序列B_1, ... , B_N,作为修过的路中每个路段的高度。由于将每一段路垫高或挖低一个单位的花费相同,修路的总支出可以表示为:
|A_1 - B_1| + |A_2 - B_2| + ... + |A_N - B_N|
请你计算一下,Mr. Yao在这项工程上的最小支出是多少。Mr. Yao向你保证,这个支出不会超过2^31-1。
动态规划:
分析一下需要知道的量:路面的位置,路面需要调整的高度,
所以我们设f[i,j]表示到了路面为i的位置,高度为j,但它怎么由前面的转移过来呢,f[i,j]:=min(f[i,k])+abs(a[i]-j)
它的状态有前方的状态转移过来:它i位置的高度为k,(K<=j),的最小代价,再加上将i位置调整到j高度的花费。
K究竟是多少呢?有贪心的思想可知,调整时调整到原来某个高度一定最好(调整到与某个路面相同的高度,保证了不降或不升,但若调整到其他高度,那么代价一定又加上了比相同高度的多余代价),开一个b[i]数组,记录a[i]的高度,排序,f[i,j]:=min(f[i,k])+abs(a[i]-b[j])中的j含义变成了第j种高度,for i:=1 to n do (阶段)
For j:=1 to n do (状态)
For k:=1 to j do (决策)
N^3的时间复杂度过不了,所以改变方程的含义f[i,j]:=min(f[i,j-1],f[i-1,j]+abs(a[i]-b[j]))此时f[i,j]表示到了路面为i的位置,高度小于或等于j,且符合题意。它由前方两种状态转移过来:1.第i-1种为j,则它的代价需要加上abs(a[i-b[j]]);2.f[i,j-1]表示j从1到n转移时记录的将第i段路高度设为小于或等于b[j-1]时最小值,每一次用当前状态表示最小,这样将时间复杂度降到了n^2;最后,由于不知道序列是不降或不升,所以将b[i]数组倒过来再做一遍不降的情况。俩次取min。
代码实现:
program exam;
var
a,tmp,c:array[0..2000] of longint;
n,ans,i:longint;
g,f:array[0..2000,0..2000] of longint;
function min(a,b:longint):longint;
begin
if a>b then
exit(b)
else
exit(a);
end;
procedure swap(var a,b:longint);
var
t:longint;
begin
t:=a;
a:=b;
b:=t;
end;
procedure qsort(l,r:longint);
var
i,j,x:longint;
begin
i:=l;
j:=r;
x:=a[(l+r) div 2];
repeat
while x>a[i] then
inc(i);
while x<a[j] then
dec(j);
if i<=j then
begin
swap(a[i],a[j]);
inc(i);
dec(j);
end;
until i>j;
if i<r then
qsort(i,r);
if l<j then
qsort(l,j);
end;
procedure dp;
var
i,j::longint;
begin
for i:=1 to n do
g[i,0]:=maxlongint; // 初始化
for i:=1 to n do
for j:=1 to n do //dp[i,j]:=min(dp[i-1,j]+abs(a[i]-c[j]),dp[i,j-1])
begin
f[i,j]:=g[i-1,j]+abs(a[i]-c[j]); // c 为排序后的数组
g[i,j]:=min(f[i,j],g[i,j-1]);
end;
ans:=min(ans,g[n,n]);
end;
begin
ans:=maxlongint;
readln(n);
for i:=1 to n do
readln(a[i]);
tmp:=a;
qsort(1,n);
c:=a;
a:=tmp;
dp;
writeln(ans);
end.