洛谷P1220 關路燈(區間dp)

2021-09-29 17:17:48 字數 2964 閱讀 1868

某一村莊在一條路線上安裝了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
輸出解釋:

題意:一共有n盞路燈和起點的編號c(即第c個路燈);接下來n行,第i行給出編號為i的路燈在數軸上的位置,路燈位置單調遞增;以為老張的速度為1m/s,所以每個路燈在此過程中的功耗就是其功率*等待時間(即:老張在關掉它之前所走的路程)求:按一定順序關掉所有路燈,在所有方案中,關掉所有路燈的過程中的功耗總和最小的方案的功耗值,樣例解釋如下:

30*1+20*4+10*5+10*11=270

思路:區間dp,這裡所謂區間dp,就是讓每個子區間達到最優,進而推出所求區間最優解。根據這個題的題意,已知最初的起點,我們可以理解為從後往前推,每次取最優,因為每次都是選擇繼續向前走關掉i+1還是從j返回關掉i,所以每次取這兩種方案的最小值,因為每個點都要經過,當經過第乙個和最後乙個時,其中間的所有點肯定都經過了(就順便關了),所以最終狀態一定是在第乙個或最後乙個,所以最後取這兩者的最小值即可,具體地說,就是:

用dp[i][j][0]表示i到j這個區間功耗的最小值(走完後老張在此區間左端點)

用dp[i][j][1]表示i到j這個區間功耗的最小值(走完後老張在此區間右端點)

狀態轉移方程:

dp[i][j][0]=min(dp[i+1][j][0]+(a[i+1]-a[i])*(sum[n]-(sum[j]-sum[i])),dp[i+1][j][1]+(a[j]-a[i])*(sum[n]-(sum[j]-sum[i])));//i到j這個區間當走完後老張的狀態在左端點的功耗的最小值即為min(其右子區間[i+1,j]的終點在左端點的最優解(當前在右子區間的左端點)+從其右子區間的左端點(a[i+1])向左當前區間左端點(a[i])的距離(a[i+1]-a[i]) * 在此走動的過程中還亮著的燈的總的功率(sum[n]-(sum[j]-sum[i]))這兩者的乘積即為該過程的總的功耗),其右子區間[i+1,j]的終點在右端點的最優解(當前在右子區間的右端點)+從其右子區間的右端點(a[j])向左當前區間左端點(a[i])的距離(a[j]-a[i])* 在此走動的過程中還亮著的燈的總的功率(sum[n]-(sum[j]-sum[i])))即:從上乙個的左向左到當前左/從上乙個的右向左到當前左

dp[i][j][1]=min(dp[i][j-1][0]+(a[j]-a[i])*(sum[n]-(sum[j-1]-sum[i-1])),dp[i][j-1][1]+(a[j]-a[j-1])*(sum[n]-(sum[j-1]-sum[i-1])));//i到j這個區間當走完後老張的狀態在右端點的功耗的最小值即為min(其左子區間[i,j-1]的終點在左端點的最優解(當前在左子區間的左端點)+從其左子區間的左端點(a[i])向右當前區間右端點(a[j])的距離(a[j]-a[i]) * 在此走動的過程中還亮著的燈的總的功率(sum[n]-(sum[j-1]-sum[i-1]))這兩者的乘積即為該過程的總的功耗),其左子區間[i,j-1]的終點在右端點的最優解(當前在左子區間的右端點)+從其左子區間的右端點(a[j-1])向右當前區間右端點(a[j])的距離(a[j]-a[j-1]) * 在此走動的過程中還亮著的燈的總的功率(sum[n]-(sum[j-1]-sum[i-1]) ))即:從上乙個的左向右到當前右/從上乙個的右向右到當前右

完整**:

#include #define int long long

using namespace std;

int n,c,a[55],p[55],sum[55],dp[55][55][2];

signed main()

memset(dp,127,sizeof(dp));//初始值設為正無窮,因為只能選擇從給定的點開始走的路線,所以設正無窮就可以把別的路線就在取min時捨去(memset設127就相當於正無窮)

dp[c][c][0]=dp[c][c][1]=0;//一定是先關起點處的,所以設為0

for(int k=2;k<=n;k++)//列舉所有區間長度(區間長度為j-i+1)

}int ans=min(dp[1][n][0],dp[1][n][1]);//取[1,n]這個區間的最優解(位於左端點的最優和右端點的最優這兩種方案的最小值)

cout<

洛谷 P1220 關路燈 區間DP

某一村莊在一條路線上安裝了 n 盞路燈,每盞燈的功率有大有小 即同一段時間內消耗的電量有多有少 老張就住在這條路中間某一路燈旁,他有一項工作就是每天早上天亮時一盞一盞地關掉這些路燈。為了給村里節省電費,老張記錄下了每盞路燈的位置和功率,他每次關燈時也都是盡快地去關,但是老張不知道怎樣去關燈才能夠最節...

洛谷P1220關路燈 區間dp

某一村莊在一條路線上安裝了 n 盞路燈,每盞燈的功率有大有小 即同一段時間內消耗的電量有多有少 老張就住在這條路中間某一路燈旁,他有一項工作就是每天早上天亮時一盞一盞地關掉這些路燈。為了給村里節省電費,老張記錄下了每盞路燈的位置和功率,他每次關燈時也都是盡快地去關,但是老張不知道怎樣去關燈才能夠最節...

洛谷 P1220 關路燈 區間dp

分析一下,明顯的區間dp,我們以dp i j 1 表示在i 到j的路燈已關,且老張在j點的情況下所用功耗的最小值,dp i j 0 則表示老張在i點,接著就是區間dp部分,見 include include include include includeusing namespace std con...