题目:
https://www.luogu.org/problemnew/show/P2885
由于把每一根电线杆增加多少高度不确定,所以很难直接通过某种方法算出答案,考虑动态规划。
状态:f [ i ] [ j ]表示当第i根电线杆的高度为j时的最小代价和。
转移:当前电线杆的高度只会影响它下一个电线杆的高度,所以就是用前一维的答案来更新后一维。在计算第i根电线杆和第i-1根电线杆时需要计算三部分答案:1.将第i根电线杆增加到j高度的代价 ( j - h [ i ])*(j - h [ i ] )。2.到第i-1根电线杆时的最小花费f [ i-1 ] [ k ](由于我们并不知道第i-1根电线杆的高度,所以需要枚举第i-1根电线杆的高度k)3. 第i根电线杆到第i-1根电线杆之间电线的代价( j - k )*C
由此我们得到转移方程:f [ i ] [ j ] = ( j - h [ i ] )^2 + min( f [ i - 1 ] [ k ] + | j - k |*C )
然而这需要我们用三重循环枚举,时间复杂度是O( n*C*C ),会超时(好像有人用这个卡过了。。。)。
优化:
我们把绝对值拆开来可以得到一个分段函数:
f [ i ] [ j ] = ( j - h [ i ] )^2 + min( f [ i - 1 ] [ k ] + j * C - k * C) ,j>=k
f [ i ] [ j ] = ( j - h [ i ] )^2 + min( f [ i - 1 ] [ k ] + k * C - j * C ) ,j < k
我们把 j * C从min中提出来,就变成了:
f [ i ] [ j ] = ( j - h [ i ] )^2 + j * C + min( f [ i - 1 ] [ k ] - k * C) ,j>=k
f [ i ] [ j ] = ( j - h [ i ] )^2 - j * C + min( f [ i - 1 ] [ k ] + k * C ) ,j < k
我们可以发现,min里面的东西只与k和i有关而与j无关,所以我们可以在外层枚举i,在内层预处理出f [ i - 1 ] [ k ] - k * C的前缀最小值和f [ i - 1 ] [ k ] + k * C的后缀最小值,然后再枚举j,这是就可以直接O(1)调用min(...)了。
同时由于每一维的状态只与上一维有关且不需要记录答案,所以可以通过滚动数组来将空间复杂度进一步优化到O(C)。
代码:(注意赋初值等细节)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int read() 4 { 5 int ans=0; 6 char ch=getchar(),last=' '; 7 while(ch<'0'||ch>'9') 8 {last=ch;ch=getchar();} 9 while(ch>='0'&&ch<='9') 10 {ans=ans*10+ch-'0';ch=getchar();} 11 if(last=='-')ans=-ans; 12 return ans; 13 } 14 const int M=1e5+10,inf=1e9+7; 15 int n,C,h[M],f[M][105],m; 16 //f[i][j]表示第i根电线杆的高度为j时的最小代价和 17 int l[105],r[105]; 18 int main() 19 { 20 n=read();C=read(); 21 memset(f,0x3f,sizeof(f)); 22 for(int i=1;i<=n;i++) 23 h[i]=read(),m=max(m,h[i]); 24 for(int i=h[1];i<=m;i++) 25 f[1][i]=(i-h[1])*(i-h[1]); 26 for(int i=2;i<=n;i++) 27 { 28 l[h[i-1]-1]=inf; 29 r[m+1]=inf; 30 for(int k=h[i-1];k<=m;k++) 31 l[k]=min(l[k-1],f[i-1][k]-k*C); 32 for(int k=m;k>=h[i];k--) 33 r[k]=min(r[k+1],f[i-1][k]+k*C); 34 for(int j=h[i-1];j<=m;j++) 35 if(j>=h[i])f[i][j]=l[j]+(j-h[i])*(j-h[i])+C*j; 36 for(int j=m;j>=h[i];j--) 37 f[i][j]=min(f[i][j],r[j]-C*j+(j-h[i])*(j-h[i])); 38 } 39 int ans=inf; 40 for(int i=h[n];i<=m;i++) 41 ans=min(ans,f[n][i]); 42 printf("%d ",ans); 43 return 0; 44 }