openjudge telephone wire

探讨了如何通过调整电线杆高度来最小化电话线改造成本的问题,采用动态规划算法解决,并提供了两种不同的实现方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2373:Telephone Wire

总时间限制: 
10000ms 
单个测试点时间限制: 
1000ms 
内存限制: 
65536kB
描述
Farmer John's cows are getting restless about their poor telephone service; they want FJ to replace the old telephone wire with new, more efficient wire. The new wiring will utilize N (2 <= N <= 100,000) already-installed telephone poles, each with some height_i meters (1 <= height_i <= 100). The new wire will connect the tops of each pair of adjacent poles and will incur a penalty cost C * the two poles' height difference for each section of wire where the poles are of different heights (1 <= C <= 100). The poles, of course, are in a certain sequence and can not be moved.

Farmer John figures that if he makes some poles taller he can reduce his penalties, though with some other additional cost. He can add an integer X number of meters to a pole at a cost of X^2.

Help Farmer John determine the cheapest combination of growing pole heights and connecting wire so that the cows can get their new and improved service.
输入
* Line 1: Two space-separated integers: N and C

* Lines 2..N+1: Line i+1 contains a single integer: height_i
输出
* 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
提示
INPUT DETAILS:

There are 5 telephone poles, and the vertical distance penalty is $2/meter. The poles initially have heights of 2, 3, 5, 1, and 4, respectively.

OUTPUT DETAILS:

The best way is for Farmer John to raise the first pole by 1 unit and the fourth pole by 2 units, making the heights (in order) 3, 3, 5, 3, and 4. This costs $5. The remaining wiring will cost $2*(0+2+2+1) = $10, for a total of $15.
来源

USACO November 2007 Gold


题目大意:农民FJ要改进他农场的电话系统,他要把他的N个电线杆进行改造。这N个电线杆的是呈线性分布的d[1-N],对于相邻的两根电线杆其改造费用是C * (两个电线杆的高度差)。另外,农民FJ也可以对任意的电线杆进行加长(比如加长X),但是要另外花费加长的费用X^2。现在你的任务是求出农民FJ改造农场所需要的最少的费用是是多少?


60分程序

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n,c,h[100003],f[100003][103];
int i,j,k,maxn,ans;
int main()
{
scanf("%d%d",&n,&c);
for (i=1;i<=n;i++)
{
scanf("%d",&h[i]);
maxn=max(maxn,h[i]);
}
memset(f,127/3,sizeof(f));
for (i=h[1];i<=maxn;i++)
f[1][i]=(i-h[1])*(i-h[1]);
for (i=2;i<=n;i++)
for (j=h[i];j<=maxn;j++)
 for (k=h[i-1];k<=maxn;k++)
  {
  int x=f[i-1][k]+c*abs(j-k)+(j-h[i])*(j-h[i]);
   f[i][j]=min(f[i][j],x);
          }
    ans=1000000000;
    for (i=h[n];i<=max(h[n-1],h[n]);i++)
      ans=min(f[n][i],ans);
printf("%d",ans); 
}//这个思路好想好实现但是100000×100×100肯定会超时,f[i][j]=min( (j-a[i])^2+f[i-1][k]+|j-k|*c ) (j>=a[i])这个是基础,在这个基础上进行优化。

表示自己比较水,没能想到优化的方法,于是参考了一下网上的思路:

DP
设f[i][j]表示第i个电线杆高度为j时的最小花费,
DP方程为f[i][j]=min( (j-a[i])^2+f[i-1][k]+|j-k|*c )
直接根据这条方程做的话复杂度是100000*100*100,明显会超时.
可以将方程进行化简:
f[i][j]=min( (j-a[i])^2+f[i-1][k]+|j-k|*c ) (j>=a[i])
       =(j-a[i])^2+j*c+min(f[i-1][k]-k*c) (j>=k)
     (j-a[i])^2-j*c+min(f[i-1][k]+k*c) (j<k)
设low[j]=min(f[i-1][k]-k*c) (j>=k)
high[j]=min(f[i-1][k]+k*c) (j<k)
方程就变为f[i][j]=(j-a[i])^2+min(high[j]-j*c,low[j]+j*c);
可以通过预处理计算先出high[j],low[j].


满分代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int f[100003],a[100003],n,m,i,j,c;
int high[103],low[103];
int main()
{
scanf("%d%d",&n,&c);
for (i=1;i<=n;i++)
scanf("%d",&a[i]);
for (i=1;i<=100;i++)
if (i<a[1])
 f[i]=1000000000;
else
 f[i]=(i-a[1])*(i-a[1]); 
for (i=2;i<=n;i++)
{
int t=1000000000;
for (j=100;j>0;j--)
 high[j]=t=min(t,f[j]+j*c);//high 存储的是原方程中J<K的状态,T中存储的其实是从100 到当前高度的最优值。

   t=1000000000;
for (j=1;j<=100;j++)
{
 low[j]=t=min(t,f[j]-j*c);//刚开始我一直不明白为什么这么处理,因为理解错了这里J的含义,注意这里的J表示的是第I个电线杆的长度,而不是原始状态转移方程中的K。
 f[j]=1000000000;
    }
for (j=a[i];j<=100;j++)
{
int x,y;
x=(j-a[i])*(j-a[i])+j*c+low[j];
   y=(j-a[i])*(j-a[i])-j*c+high[j];
   f[j]=min(x,y);
}

int ans=1000000000;
for (i=1;i<=100;i++)
ans=min(ans,f[i]);
printf("%d",ans);
return 0;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值