P2885 [USACO07NOV]电话线Telephone Wire——Chemist

题目:

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\n",ans);
43     return 0;
44 }
View Code

猜你喜欢

转载自www.cnblogs.com/nopartyfoucaodong/p/9419006.html