1.10关路灯

来源:互联网 发布:android开发想找php 编辑:程序博客网 时间:2024/04/27 16:18
 

1.10关路灯     

源程序名            power.???(pas, c, cpp)

可执行文件名        power.exe

输入文件名          power.in

输出文件名          power.out

【问题描述】

    某一村庄在一条路线上安装了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

【算法分析】

    设老张开始所在位置为c,以起始点c为分界点,算出左右两部分总的功率p_left和p_right,再来分别看向左与向右的情况。

    向左走时,相应地可以减小左边应费的功,而增加右边应费的功,如果到一个点(一盏路灯处)所要时间为t,减少的功为(p_left+w[i])*t,增加的功为p_right*2t。

    向右走时,相应地可以减小右边应费的功,而增加左边应费的功,如果到一个点(一盏路灯处)所要时间为t,减少的功为(p_righ+w[i])*t,增加的功为p_left*2t。

    比较向左与向右的情况,找出比较好的一种确定方法。大部分情况能够解出最小值,但不能求出所有情况下最佳的解。

    对于每一个所处位置,都可以选择向左或向右,不管是向左还是向右,相应耗电的变化都跟上面所述一样。所以可以选择回溯的算法来实现有限的搜索,对每一个点试探向左与向右的情况,在所有可能的情况中找出最优解。

【思考与提高】

    上面的程序运算的范围很有限,当n比较大时就会栈溢出,如n>30时速度就比较慢了。实际情况调头的次数并不会多的,到底在什么时候掉头根据情况而定。我们可以从最后一步来思考:

    最后一次关的可能是第一个灯也可能是最后一个灯,哪种情况所费的功小就选哪种;

    最后一次关的是第一个灯的话,说明最后的方向是从最后到最前(右边到左边),最后倒数第二次的方向为从左到右,起点可能是原始起点(此时是第一趟),也可能是原始起点左边的点(此时至少是第二趟),一个个地试过去,先设拐一次弯,有可能拐的点都试过去,再试有两次拐弯换方向的情况,当再多的拐弯超过已有的解时就不要再向下试了。采用这种回溯方法,效率更高。

    如果n再大一些,如到300以上,上述方法也有它的局限性,此时最好从动态规划法或贪心法的角度去思考。

 

上面是书上的题解

 

var f1,f2:text;n,i,j,c,ans,sum:longint;dx,a:array[0..50] of longint;b:array[0..50] of boolean;procedure f(w:longint);begin if w<ans then ans:=w;end;procedure find(x,p,w:longint);var k:longint;begin if w>=ans then exit; for k:=x-1 downto 1 do  if not b[k] then   begin    b[k]:=true;    if p-a[k]=0 then f(w+abs(dx[x]-dx[k])*p)    else    find(k,p-a[k],w+abs(dx[x]-dx[k])*p);    b[k]:=false;    break;   end; for k:=x+1 to n  do  if not b[k] then   begin    b[k]:=true;    if p-a[k]=0 then f(w+abs(dx[x]-dx[k])*p)    else    find(k,p-a[k],w+abs(dx[x]-dx[k])*p);    b[k]:=false;    break;   end;end;begin assign(f1,'input.in'); assign(f2,'output.out'); reset(f1); rewrite(f2); readln(f1,n,c); for i:=1 to n do  begin  readln(f1,dx[i],a[i]);  sum:=sum+a[i];  end; ans:=maxlongint; b[c]:=true; find(c,sum-a[c],0); writeln(f2,ans); close(f1); close(f2);end.


 

原创粉丝点击