关路灯、Grazing on the run---很难的dp

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)语句将腐烂度的计算

分离了出来,变成了在大局上对总体腐烂度的考虑,从而免去了对单个牧草腐烂度的计算的繁琐过程

代码如下:

 

代码
1 #include<stdio.h>
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或题目要求的数据上限。

 

 

 

                     

posted @ 2010-09-28 11:07  Danty  阅读(676)  评论(0编辑  收藏  举报