1. 程式人生 > 實用技巧 >P1220關路燈(題解)

P1220關路燈(題解)

題目描述

某一村莊在一條路線上安裝了n盞路燈,每盞燈的功率有大有小(即同一段時間內消耗的電量有多有少)。老張就住在這條路中間某一路燈旁,他有一項工作就是每天早上天亮時一盞一盞地關掉這些路燈。為了給村裡節省電費,老張記錄下了每盞路燈的位置和功率,他每次關燈時也都是儘快地去關,但是老張不知道怎樣去關燈才能夠最節省電。他每天都是在天亮時首先關掉自己所處位置的路燈,然後可以向左也可以向右去關燈。開始他以為先算一下左邊路燈的總功率再算一下右邊路燈的總功率,然後選擇先關掉功率大的一邊,再回過頭來關掉另一邊的路燈,而事實並非如此,因為在關的過程中適當地調頭有可能會更省一些。現在已知老張走的速度為1m/s,每個路燈的位置(是一個整數,即距路線起點的距離,單位:m)、功率(W),老張關燈所用的時間很短而可以忽略不計。請你為老張編一程式來安排關燈的順序,使從老張開始關燈時刻算起所有燈消耗電最少(燈關掉後便不再消耗電了)。

輸入輸出格式

輸入格式:

檔案第一行是兩個數字n(1<=n<=50,表示路燈的總數)和c(1<=c<=n老張所處位置的路燈號);
接下來n行,每行兩個資料,表示第1盞到第n盞路燈的位置和功率。資料保證路燈位置單調遞增。

輸出格式:

一個數據,即最少的功耗(單位:J,1J=1W·s)。

輸入輸出樣例

輸入樣例#1:

5 3
2 10
3 20
5 20
6 30
8 10

輸出樣例#1:

270

說明

輸出解釋:
{此時關燈順序為3 4 2 1 5,不必輸出這個關燈順序}

冷靜的分析:顯然是區間DP,可以轉化成一個個區間進行處理,每個區間2種情況

*沿著當前方向繼續走下去。
*改變方向回去關燈。
f[i][j]表示i到j燈熄滅後的剩餘總功率,進一步區分老王的站位,就再加一維f[i][j][0]表示老王在i點,f[i][j][1]表示老王再j點。
f[i][j][0]的狀態來源於上面2種情況則:
f[i][j][0] = min ( f[i+1][j][0] + ( a[i+1] - a[i] ) * ( sum[i] + sum[n] - sum[j] ) , f[i+1][j][1] + ( a[j]-a[i] ) * ( sum[i]+sum[n]-sum[j]) );
f[i][j][1] = min ( f[i][j-1][0] + ( a[j] - a[i] ) * ( sum[i-1] + sum[n] - sum[j-1] ) , f[i][j-1][1] + ( a[j]-a[j-1] ) * ( sum[i-1] + sum[n] - sum[j-1] ) );
(列舉現在的路燈l(2-n,因為第c位的路燈已經被關了),i+l-1<=n(路只有這麼長),j=i+l-1(右端點))
因為最後不知道老張到底站在左端點最優還是站在右端點最優,所以在f[1][n][0]和f[1][n][1]中取min輸出。要求功率消耗最小。

程式碼:

#include <cstdio>
#include <cstring>
using namespace std;
const int MAXM=60;
int a[MAXM],b[MAXM],sum[MAXM],n,m,c;
int f[MAXM][MAXM][2];
int min(int a,int b)//這一點希望大家注意:最好max和min函式自己寫,會有效的加快程式速度
{return a<b?a:b;}
int max(int a,int b)
{return a>b?a:b;}
int main()
{
  scanf("%d%d",&n,&c);
  memset(f,127,sizeof(f));//賦成極大值防止後面的min出問題
  for(int i=1;i<=n;i++)
    scanf("%d%d",&a[i],&b[i]),sum[i]=sum[i-1]+b[i];//計算功率總和
  f[c][c][0]=f[c][c][1]=0;//瞬初始化
  for(int l=2;l<=n;l++)//列舉n個燈
    for(int i=1;i+l-1<=n;i++)//路長
      {
    int j=i+l-1;//右端點
    f[i][j][0]=min(f[i+1][j][0]+(a[i+1]-a[i])*(sum[i]+sum[n]-sum[j]),//繼續走下去會更快嗎?
               f[i+1][j][1]+(a[j]-a[i])*(sum[i]+sum[n]-sum[j]));//還是從j點折返回來會更快?(此時假設[i+1][j]被關,i亮,從j端點往回趕去關i)
//要注意的一點是sum[n]-(sum[j]-sum[i])是包括了i這一點的電能的,因為走過來的過程中燈i也會耗電
    f[i][j][1]=min(f[i][j-1][0]+(a[j]-a[i])*(sum[i-1]+sum[n]-sum[j-1]),//同上
               f[i][j-1][1]+(a[j]-a[j-1])*(sum[i-1]+sum[n]-sum[j-1]));
        }
  int ans=min(f[1][n][0],f[1][n][1]);
  printf("%d",ans);
  return 0;
}

雖然渺小,但不放棄。