C 山区修路
- 内存限制:128MB 时间限制:3s Special Judge: No
题目描述:
SNJ位于HB省西部一片群峰耸立的高大山地,横亘于A江、B水之间,方圆数千平方公里,相传上古的神医在此搭架上山采药而得名。景区山峰均在海拔3000米以上,堪称"华中屋脊"。SNJ是以秀绿的亚高山自然风光,多样的动植物种,人与自然和谐共存为主题的森林生态区。
SNJ处于中国地势第二阶梯的东部边缘,由大巴山脉东延的余脉组成中高山地貌,区内山体高大,高低不平。 交通十分不便。
最近,HB省决定修一条从YC市通往SNJ风景区的高速公路。经过勘测分析,途中需要经过高度分别为H1,H2,……,Hn的N个山区。由于高低不平,除正常的修路开支外,每段还要多出高度差|Hi - Hi-1|*X万元的斜坡费用。Dr. Kong 决定通过填高一些区域的高度来降低总的费用。当然填高也是需要一些费用的。每填高Y单位,需要付出Y2万元费用。
你能否帮Dr. Kong做出一个规划,通过部分填高工程改造,使得总的费用降下来。
输入描述:
第一行: T 表示以下有T组测试数据( 1≤ T ≤8 ) 对每组测试数据, 第一行:N X(2 ≤ N ≤100,000 1≤ X ≤100) 第二行:N个整数,分别表示N个区域的高度Hi( 1<=Hi<=100 , i=1…. n)
输出描述:
对每组测试数据,输出占一行,一个整数,即经过部分填高工程改造后的最少费用。
样例输入:
复制
1 5 2 2 3 5 1 4
样例输出:
15
提示:
来源:
河南省第十一届ACM大学生程序设计竞赛
思路:当时比赛时,怎么都想不出来,怎么dp。当时我第一步,想推的两个高度之间怎么去做的,让他们花费小一点,分析花费方程,垫高:y = x^2,若修斜坡 y = (hi - hj) * k,k为题目上的X,画图分析斜率,也就上增加的速度,2 *x = k ,x = k/2 时,上升相等,当x<k/2时,垫高花费少,x>k/2,时,前k/2的高度,用垫高,x - k/2 的高度,用修斜坡。这样花费最少,但是其实分析到这,对真正做出这道题,是没有用的。
分析一下高度,区域高度只能增加或者不变。
其实知道一共最多有多少种状态 dp[10000][100][100],一共也就这么多种状态 dp[i][j][k] 前i个,高度为j时的最小花费,dp[i][j][k] i表示第几个,j表示当前的高度,k表示i-1的区域的高度,表示是从i-1区域高度为k转移到 i区域高度为j时的最小花费,但是就是这样想的,但是想想三层for循环。没敢写,有些后悔
比赛完回来,想想其实也想错,其实二维的就够了,但是状态数还是那么多,dp[i][j] 存前i个区域的当前区间高度为j时的最小花费。dp[i][j] 一定是由dp[i-1][1~100] 中转移过来的,我就开始按照,我比赛时推的那个斜率开始写了,但是,出现问题了,你这个递推方程的参照物就是 两个相邻区域高度,若垫高i-1的区域的高度,一定会影响前面的dp数组中的推出的值,若垫高当前区域的高度,那么求出的就不是dp[i][j] 的值了(dp[i][j+垫高的高度]),这样好乱或者根本没法写
最终我,我得出了一个方案,就是先垫高,因为你既然要求dp[i][j],若j>a[i],你一定要去从a[i]的高度去垫高高j的高度。所以先垫高,然后状态转移过程中,高度不会变,只加斜坡。
最后就是时间的问题:当for循环次数多的时候,在for中上加上这个单词 register 会快很多。
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Max 100010
#define INF 0x3f3f3f3f
int dp[Max][105]; //dp[i][j] 前i个区域,高度为j时的修路的最小花费。
int a[Max];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int x,n;
scanf("%d%d",&n,&x);
int Ma = 0;
for(register int i = 1;i<=n;i++)
{
scanf("%d",&a[i]);
Ma = max(Ma,a[i]);
}
memset(dp[0],0,sizeof(dp[0]));
for(register int i = 0;i<=100;i++)
dp[i][0] = 0;
for(register int i = 1;i<=n;i++)
{
for(register int j = a[i];j<=Ma;j++)
{
dp[i][j] = (j-a[i])*(j-a[i]); // 先垫高
int tt = INF;
for(register int k = a[i-1];k<=Ma;k++)
tt = min(dp[i-1][k] + abs(j-k)*x,tt); // 然后修斜坡
dp[i][j] += tt;
}
}
int ans = INF;
for(register int i = a[n];i<=Ma;i++)
ans = min(ans,dp[n][i]);
printf("%d\n",ans);
}
return 0;
}