[USACO07NOV]电话线Telephone Wire

[USACO07NOV]电话线Telephone Wire

时间限制: 1 Sec  内存限制: 128 MB

题目描述

电信公司要更换某个城市的网线。新网线架设在原有的 N(2 <= N <= 100,000)根电线杆上, 第
i 根电线杆的高度为 height_i 米(1 <= height_i <= 100)。 网线总是从一根电线杆的顶端被引到
相邻的那根的顶端,如果这两根电线杆的高度不同,那么电信公司就必须为此支付 C*电线
杆高度差(1 <= C <= 100)的费用。电线杆不能移动, 只能在相邻电线杆间按原有的顺序架设
网线。加高某些电线杆能减少架设网线的总花费,但需要支付一定的费用,一根电线杆加高
X 米的费用是 X^2。 请你计算一下,如何合理地进行这两种工作,使网线改造工程的最小费
用。

输入

 

 

  • Line 1: Two space-separated integers: N and C

  • Lines 2..N+1: Line i+1 contains a single integer: heighti

 

输出

 

 

  • Line 1: The minimum total amount of money that it will cost Farmer John to attach the new telephone wire.

 

样例输入

5 2
2
3
5
1
4

样例输出

15
题解:
f[i][j]表示第i个电线杆高度为j时所需要的最少的费用。
然后很快就可以得出暴力代码,每次枚举上一个电线杆的高度就可以了。
先付上暴力代码:(TLE到爆表)
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<queue>
 8 #include<stack>
 9 #include<ctime>
10 #include<vector>
11 using namespace std;
12 long long n,m;
13 long long a[100001],f[100001][101],mmax;
14 int main()
15 {
16     long long i,j,k;
17     scanf("%lld%lld",&n,&m);
18     memset(f,127/3,sizeof(f));
19     for(i=1;i<=n;i++)
20     {
21         scanf("%lld",&a[i]);
22         mmax=max(mmax,a[i]);
23     }
24     for(i=a[1];i<=mmax;i++)
25     {
26         int s=i-a[1];
27         f[1][i]=s*s;
28     }
29     for(i=1;i<=n;i++)
30     {
31         for(j=a[i-1];j<=mmax;j++)
32         {
33             for(k=a[i];k<=mmax;k++)
34             {
35                 int s=k-a[i];
36                 f[i][k]=min(f[i][k],s*s+f[i-1][j]+m*abs(j-k));
37             }
38         }
39     }
40     long long ans=2000000000000000000;
41     for(i=a[n];i<=mmax;i++)
42     ans=min(ans,f[n][i]);
43     cout<<ans;
44     return 0;
45 }

显然是需要优化的,仔细想一想就可以看出,每次实际上只有两种情况:

1.i-1的高度比i低。

2.i-1的高度比i高。

第一种情况下f[i][j]的结果为f[i-1][min]+abs(j-min)*k+(j-a[i])^2显然是有最小值的,所以只要记录min就可以直接算出f[i][j]的值。

第二种情况下f[i][j]的结果为f[i-1][min]+abs(j-min)*k+(j-a[i])^2,但由于随着j的增加每次min的值都有可能会改变,所以需要用到一个单调队列来记录最小值。

以下为AC代码:

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<queue>
 8 #include<stack>
 9 #include<ctime>
10 #include<vector>
11 using namespace std;
12 long long n,m;
13 long long a[100001],f[100001][101],mmax;
14 int main()
15 {
16     long long i,j,k;
17     scanf("%lld%lld",&n,&m);
18     memset(f,127,sizeof(f));
19     for(i=1; i<=n; i++)
20     {
21         scanf("%lld",&a[i]);
22         mmax=max(mmax,a[i]);
23     }
24     for(i=a[1]; i<=mmax; i++)
25     {
26         int s=i-a[1];
27         f[1][i]=s*s;
28     }
29     for(i=2; i<=n; i++)
30     {
31         int p[101],head=1,tail=0,mmin=0;
32         for(j=a[i-1];j<a[i];j++)
33         {
34             if(f[i-1][j]+abs(a[i]-j)*m<f[i-1][mmin]+abs(a[i]-mmin)*m||mmin==0)
35             mmin=j;
36         }
37         int ssss=max(a[i],a[i-1]);
38         p[++tail]=ssss;
39         for(j=ssss+1;j<=mmax;j++)
40         {
41             while(f[i-1][j]+abs(j-a[i])*m<f[i-1][p[tail]]+abs(p[tail]-a[i])*m&&head<=tail)tail--;
42             p[++tail]=j;
43         }
44         for(j=a[i];j<=mmax;j++)
45         {
46             f[i][j]=min(f[i][j],f[i-1][mmin]+abs(j-mmin)*m+(j-a[i])*(j-a[i]));
47             f[i][j]=min(f[i][j],f[i-1][p[head]]+abs(j-p[head])*m+(j-a[i])*(j-a[i]));
48             if(f[i-1][mmin]-abs(j-mmin)*m>f[i-1][j])mmin=j;
49             if(p[head]==j)head++;
50         }
51     }
52     long long ans=1e18;
53     for(i=a[n]; i<=mmax; i++)
54     ans=min(ans,f[n][i]);
55     cout<<ans;
56     return 0;
57 }

 

 

 

posted @ 2017-06-06 11:00  kakakakakaka  阅读(228)  评论(1编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效