Grazing on the run与关路灯其实是同一道题,但有小小的不同,这是Grazing on the run的原题:
http://acm.pku.edu.cn/JudgeOnline/problem?id=3042
中文翻译题目如下:
牧草
问题描述:
数轴上有N(1 <= N <= 1,000)株牧草,Bessia从L(1 <= L <= 1,000,000)点出发,速度为每秒一个单位距离,方向为左或右。已知牧草的腐烂程度等于吃掉它时的时间,求一种吃掉所有牧草的方法,使得腐烂度之和最小。
输入格式:
第1行,两个整数N和L。
接下N行,每行一个整数,表示N株牧草的位置。
输出格式:
一个整数,吃完所有牧草的最小腐烂度。
样例输入:
4 10
1
9
11
19
样例输出:
44
样例说明:
Bessie 按如下路线走:
l 开始点为10
l 走到 9, 时间为1
l 走到11, 到达时间为3
l 走到19,到达时间为11
l 走到 1, 到达时间为 29
关路灯的题目如下:
关路灯
【问题描述】
某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少)。老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯。
为了给村里节省电费,老张记录下了每盏路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能够最节省电。他每天都是在天亮时首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯。开始他以为先算一下左边路灯的总功率再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头来关掉另一边的路灯,而事实并非如此,因为在关的过程中适当地调头有可能会更省一些。
现在已知老张走的速度为1m/s,每个路灯的位置(是一个整数,即距路线起点的距离,单位:m)、功率(W),老张关灯所用的时间很短而可以忽略不计。
请你为老张编一程序来安排关灯的顺序,使从老张开始关灯时刻算起所有灯消耗电最少(灯关掉后便不再消耗电了)。
【输入】
文件第一行是两个数字n(0<n<50,表示路灯的总数)和c(1<=c<=n老张所处位置的路灯号);
接下来n行,每行两个数据,表示第1盏到第n盏路灯的位置和功率。
【输出】
一个数据,即最少的功耗(单位:J,1J=1W·s)。
【样例】
power.in power.out
5 3 270 {此时关灯顺序为3 4 2 1 5,不必输出这个关灯顺序}
2 10
3 20
5 20
6 30
8 10
两道题目的
相同点:
1.都是在一条直线上移动的问题,可选方向都有两个,向左或向右;
2.速度都为1m/s;
3.路灯的消耗功率会随时间的流逝而增加,牧草的腐烂度也是如此。关路灯中每个灯每秒都消耗的功,牧草中每株牧草每秒的腐烂度也在增加,所以实际上可以理解为,
牧草的腐烂“功率”为1,对应于关路灯中的路灯的功率。
不同点:
关路灯的起点一定是在某一个路灯旁边,而牧草的起点不一定。这就直接导致了两道题目要采用不同的赋初值方法。
两个程序都需注意的问题:
样例数据给出的牧草(或路灯)定是从左到右的顺序,但题目并没有说牧草(或路灯)一定是顺序给出的,测试数据中牧草(或路灯)有可能是乱序,所以有必要对所有的路灯排序;
先拿牧草问题分析本题的dp:
f[i][j][0]=min{f[i+1][j][1]+a[i][j]*(i+n-j),f[i+1][j][0]+a[i][i+1]*(i+n-j)}
f[i][j][1]=min{f[i][j-1][1]+a[j][j-1]*(i+n-j),f[i][j-1][0]+a[j][i]*(i+n-j)}
f[i][j][0]代表吃完从i到j的所有牧草,最后吃第i株牧草得到的总的最小腐烂度,并不只是i到j之间的牧草的最小腐烂度之和,还有i以前和j以后的所有未采的牧草的腐烂度。f[i][j][1]的意义与之相似。
a[i][j]代表第i株牧草与第j株牧草间的距离,也就是从i到j的时间。
先明白一个决策:关掉的灯必然是一个连续的区间,也就是说,在路过的时候肯定会把灯顺手关掉,不然肯定不是最优解。所以i点的最小腐烂度可以由i+1或j得到,j点的最小腐烂度可以由j-1或i得到。
然后解决腐烂度的问题。
a[i][j]*(i+n-j)该语句解决的就是腐烂度的问题。拿一条语句分析:
f[i][j][0]=min{f[i+1][j][1]+a[i][j]*(i+n-j),f[i+1][j][0]+a[i][i+1]*(i+n-j)}
从i+1到j用时a[i][j],期间共有包括i在内的i+n-j株(i以前包括i有i株,j以后不包括j有n-j株)牧草的腐烂度累加了a[i][j]的程度,故总的腐烂度为f[i+1][j][1]+a[i][j]*(i+n-j)。
为什么要设计这样的f[i][j][]呢?我们的目标是f[1][n][0]和f[1][n][1],这样的f[i][j][]就保证了在设计过程中我们需要的牧草的腐烂度是随时间增加的,也就是说,f[i+1][j][1]+a[i][j]*(i+n-j)语句将腐烂度的计算
分离了出来,变成了在大局上对总体腐烂度的考虑,从而免去了对单个牧草腐烂度的计算的繁琐过程。
代码如下:
2 #include < stdlib.h >
3 #include < string .h >
4 #include < math.h >
5 int f[ 1001 ][ 1001 ][ 2 ],n,l;
6 int a[ 1001 ][ 1001 ],b[ 1001 ];
7 FILE * in , * out ;
8 void order( int low, int high) //快速排序
9 {
10 int i,j;
11 i = low;j = high;
12 while (i < j)
13 {
14 while (i < j && b[i] < b[j])i ++ ;
15 if (i < j)
16 {
17 b[ 0 ] = b[i];
18 b[i] = b[j];
19 b[j] = b[ 0 ];
20 j -- ;
21 }
22 while (i < j && b[i] < b[j])j -- ;
23 if (i < j)
24 {
25 b[ 0 ] = b[i];
26 b[i] = b[j];
27 b[j] = b[ 0 ];
28 i ++ ;
29 }
30 }
31 if (i + 1 < high)order(i + 1 ,high);
32 if (i - 1 > low)order(low,i - 1 );
33 }
34 int main(){
35 in = fopen( " input.in " , " r " );
36 out = fopen( " output.out " , " w " );
37 fscanf( in , " %d%d " , & n, & l);
38 int i,j,k,min;
39 for (i = 1 ;i <= n;i ++ )
40 fscanf( in , " %d " , & b[i]);
41
42 order( 1 ,n);
43
44 for (i = 1 ;i <= n;i ++ ) //预处理i与j间的距离
45 for (j = 1 ;j <= n;j ++ )
46 a[i][j] = a[j][i] = abs(b[i] - b[j]);
47
48 for (i = 1 ;i <= n;i ++ ) //初始化
49 f[i][i][ 0 ] = f[i][i][ 1 ] = abs(l - b[i]) * n;
50
51 for (k = 2 ;k <= n;k ++ )
52 for (i = 1 ;i <= n - k + 1 ;i ++ )
53 {
54 j = i + k - 1 ;
55 if (f[i + 1 ][j][ 1 ] + a[i][j] * (i + n - j) > f[i + 1 ][j][ 0 ] + a[i][i + 1 ] * (i + n - j))
56 f[i][j][ 0 ] = f[i + 1 ][j][ 0 ] + a[i][i + 1 ] * (i + n - j);
57 else
58 f[i][j][ 0 ] = f[i + 1 ][j][ 1 ] + a[i][j] * (i + n - j);
59
60 if (f[i][j - 1 ][ 1 ] + a[j][j - 1 ] * (i + n - j) > f[i][j - 1 ][ 0 ] + a[j][i] * (i + n - j))
61 f[i][j][ 1 ] = f[i][j - 1 ][ 0 ] + a[j][i] * (i + n - j);
62 else f[i][j][ 1 ] = f[i][j - 1 ][ 1 ] + a[j][j - 1 ] * (i + n - j);
63 }
64 min = 1 << 30 ;
65 if (min > f[ 1 ][n][ 0 ])
66 min = f[ 1 ][n][ 0 ];
67 if (min > f[ 1 ][n][ 1 ])
68 min = f[ 1 ][n][ 1 ];
69 // printf("%d\n",min);system("pause");
70 fprintf( out , " %d\n " ,min);
71 fclose( in );
72 fclose( out );
73 return 0 ;
74 }
75
初始化时有两种写法:1.将起点一同并到牧草位置中,n++,然后一起排序。最后初始化如下:
for(i=1;i<=n;i++)
f[i][i][0]=f[i][i][1]=(1<<30);
f[c][c][0]=f[c][c][1]=0;
2.如上面程序中的初始化,不将起点并入牧草位置,它的初始化就是从起点走到牧草的时间,乘以需要累积腐烂度的牧草数n.
总结:做题时思维要严密,就像该题,题目没有说数据是如何给出的,就要想到数据的可能给出形式,像乱序还是顺序的,有没有0或题目要求的数据上限。