$POJ3666\ Making\ the\ Grade$
$Description$
农夫约翰想改造一条路,原来的路的每一段海拔是$Ai$,修理后是$Bi$,花费$|Ai – Bi|$。我们要求修好的路是单调不升或者单调不降的。求最小花费。
$Solution$
首先只讨论数列不降的情况
结论:$B$中的数一定都是$A$中的数
这里可以用数学归纳法证明
证明: 若n=1,显然b[1]=a[1]时最优
假设n-1时结论成立,那么n时:
当a[n]>=b[n-1]时,显然令b[n]=a[n]最优
当a[n]<b[n-1]时,由于数列不降,令b[n]=b[n-1]最优
这时产生的贡献是b[n-1]-a[n],假设b[n]>b[n-1],则产生贡献更大,没有b[n]=b[n-1]优
最后由数学归纳法可知,原命题成立
有这个结论就好做多啦
另设一个数组$C$表示数组$A$从小到大排序并且离散化后的序列
$f[i][j]$表示将$A1...Ai$变成$B1..Bj$这些数的最小代价
转移:
$f[i][j]=min(f[i-1][1...j])+abs(Ai-Bj)$
这里的$min(f[i-1][1...j])$也是在循环中记录更新即可
数列不升的情况:只要把$B$数组重新排列一下,也就是从大到小排列然后再做一遍上面的转移就可以啦
$Code$
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #define Rg register 5 #define il inline 6 #define db double 7 #define ll long long 8 #define go(i,a,b) for(Rg int i=a;i<=b;i++) 9 #define yes(i,a,b) for(Rg int i=a;i>=b;i--) 10 #define inf 1e40 11 using namespace std; 12 il int read() 13 { 14 int x=0,y=1;char c=getchar(); 15 while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();} 16 while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();} 17 return x*y; 18 } 19 const int N=2001; 20 int m,n,a[N],b[N],c[N]; 21 ll f[N][N],ans=inf; 22 il bool cmp(int x,int y){return x>y;} 23 il void sol() 24 { 25 go(i,1,n)f[1][i]=abs(b[i]-a[1]); 26 go(i,2,n) 27 { 28 ll t=inf; 29 go(j,1,m) 30 { 31 t=min(t,f[i-1][j]); 32 f[i][j]=t+abs(a[i]-b[j]); 33 } 34 } 35 go(j,1,m)if(f[n][j]<ans)ans=f[n][j]; 36 } 37 int main() 38 { 39 n=read();go(i,1,n)a[i]=c[i]=read(); 40 sort(c+1,c+n+1); 41 go(i,1,n)if(c[i]!=b[m]||i==1)b[++m]=c[i]; 42 sol(); 43 sort(b+1,b+m+1,cmp);sol(); 44 printf("%lld\n",ans); 45 return 0; 46 }
光伴随的阴影