某一村莊在一條路線上安裝了 \(n\) 盞路燈,每盞燈的功率有大有小(即同一段時間內消耗的電量有多有少)。老張就住在這條路中間某一路燈旁,他有一項工作就是每天早上天亮時一盞一盞地關掉這些路燈。
為了給村里節省電費,老張記錄下了每盞路燈的位置和功率,他每次關燈時也都是盡快地去關,但是老張不知道怎樣去關燈才能夠最節省電。他每天都是在天亮時首先關掉自己所處位置的路燈,然後可以向左也可以向右去關燈。開始他以為先算一下左邊路燈的總功率再算一下右邊路燈的總功率,然後選擇先關掉功率大的一邊,再回過頭來關掉另一邊的路燈,而事實並非如此,因為在關的過程中適當地調頭有可能會更省一些。
現在已知老張走的速度為 \(1m/s\),每個路燈的位置(是乙個整數,即距路線起點的距離,單位:\(m\))、功率(\(w\)),老張關燈所用的時間很短而可以忽略不計。
請你為老張編一程式來安排關燈的順序,使從老張開始關燈時刻算起所有燈消耗電最少(燈關掉後便不再消耗電了)。
第一行是兩個數字\(n\)(表示路燈的總數)和 \(c\)(老張所處位置的路燈號);
接下來 \(n\) 行,每行兩個資料,表示第 \(1\) 盞到第 \(n\) 盞路燈的位置和功率。資料保證路燈位置單調遞增。
乙個資料,即最少的功耗(單位:\(j\),\(1j=1w×s\))。
輸入輸出樣例
5 32 10
3 20
5 20
6 30
8 10
樣例解釋
此時關燈順序為 3 4 2 1 5。
資料範圍
\(1≤n≤50,1≤c≤n\)
這個題從題目的意思,應該挺容易就能看出來是乙個區間\(dp\),因為題意就是乙個大爺在乙個「區間」裡關燈。所以就可以根據區間長度來進行狀態轉移。首先想到如果大爺關燈從\(i\)到\(j\),當然這並不代表關燈順序,大爺關燈肯定是走過的路上的燈都關了,因為這肯定比走過不關再回來關要優。而在\(i\)到\(j\)這一段關了燈的區間裡,大爺有兩種位置情況,一種是在\(i\),也就是左邊,另一種就是右邊,所以我們的dp陣列就可以根據這個來開,也就是\(dp[i][j][0]\)和\(dp[i][j][1]\)分別表示關了i,j之間的燈,然後在最左和最右兩種位置的情況,而區間長度肯定是從最短到最長,最短為1,然後依次增加,所以每一次的\(dp[i][j]\)的狀態都是從上乙個轉移下來的,也就是\(dp[i+1][j]\)和$dp[i][j-1],然後分別在左右端點兩種,依次進行狀態轉移。而能耗的增量可以通過時間和區間裡能耗的字首和來進行轉移,下邊我用乙個轉移方程來進行一下具體說明:
首先是個轉移方程:
\[dp[i][j][0] = min(dp[i+1][j][0]+(pos[i+1] - pos[i])\times sum[i+1][j],dp[i+1][j][1]+(pos[j]-pos[i])\times sum[i+1][j])
\]在這裡,\(pos\)代表位置(這裡的位置說的是下標,不是距離,但是pos陣列存的是距離,用來計算時間)\(sum\)陣列代表的是從\(i\)到\(j\)之外的能耗,\(sum\)需要乙個預處理,下邊單獨說,這裡先介紹含義,方便理解。這個狀態轉移方程的意思也就是\(i\)到\(j\)區間內,大爺在左邊的時候,通過不同的上個狀態的位置來進行轉移,值得一提的是,因為此時大爺在左邊界,所以肯定是由\(dp[i+1][j]\)轉移而來,假如是由\(dp[i][j-1]\)轉移來的話,應當在右側,這就是下乙個狀態轉移方程。繼續看這個方程,從\(dp[i+1][j]\)轉移而來,所以也有兩種,左右邊界各一種,在左邊界時,他所需的時間就是\(pos[i+1] - pos[i]\),右邊界時就是\(pos[j]-pos[i]\),而花費的功率就是sum乘以時間,分別為:\((pos[i+1] - pos[i])\times sum[i+1][j]\)和\((pos[j]-pos[i])\times sum[i+1][j]\),看到這裡可能有人會有疑惑,從\(i\)到\(j\)之外的能耗為啥是\(i+1\)到\(j\)呢,現在我們來說一下\(sum\)的得出:我們先用\(val\)陣列當做字首和,區間\(i\)到\(j\)的能耗就是\(val[j]-val[i-1]\),\(sum[i][j]\)就是用\(val[n]\)減去上邊的能耗,具體見**。現在大概就都解釋清楚了。然後就是兩個關鍵的狀態轉移方程:
\[dp[i][j][0] = min(dp[i+1][j][0]+(pos[i+1] - pos[i])*sum[i+1][j],dp[i+1][j][1]+(pos[j]-pos[i])*sum[i+1][j]) \\
dp[i][j][1] = min(dp[i][j-1][1]+(pos[j]-pos[j-1])*sum[i][j-1],dp[i][j-1][0]+(pos[j]-pos[i])*sum[i][j-1])\]
就這樣了,然後看一下**加深一下理解⑧
#includeusing namespace std;
const int maxn = 55;
int pos[maxn];
int sum[maxn][maxn];
int dp[maxn][maxn][3];
int v[maxn];
int n,c;
int main()
memset(dp,0x3f,sizeof(dp));
dp[c][c][0] = dp[c][c][1] = 0;
for(int i=1;i<=n;++i)
} for(int j = c;j<=n;++j)
} int ans = min(dp[1][n][0],dp[1][n][1]);
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...