1. 程式人生 > >關路燈

關路燈

|| 多少 最優 AI 通過 line 枚舉 位置 能耗

關路燈

洛谷題號:P1220

出處: ?

主要算法:區間 DP

難度:4.4

https://www.luogu.org/problemnew/show/P1220

思路分析:

發現二維的區間DP始終做不了,因為不知道老王的最後位置在哪兒。那麽既然二維不行,就增加一位記錄位置!

仔細分析發現,在節省時間的前提下,關完一個區間的所有燈之後老王一定會在區間的左端點或右端點上。(它不可能關完整個區間以後再中間,不然它路過的時候幹嘛不順便關掉?)。於是就可以定義f[i][j][b]表示在關閉區間i-j的最小能耗。其中b是個布爾,若b==0代表完成以後老王到了左端點,b==1代表到了右端點。這樣就考慮了所有情況,並且答案一定是Min(f[1][n][0], f[1][n][1])。

考慮如何轉移狀態。由於題目要求老王從c出發,所以所有與c沒有交集的區間都是不可完成的,直接跳出即可。所以在區間dp枚舉區間的時候,左邊界從c開始倒著掃,右邊界從c開始正著掃就好了。在這個過程中,由於對於每一個區間有兩個狀態左邊和右邊,每個狀態有兩種轉移方法,所以總共四個狀態轉移方程。

註意這裏不用枚舉中介值,因為我們可以通過從前一次的最右邊或最左邊掉頭直接獲得最優值。

對於f[i][j][0]的,既然關閉完成以後要到達左側,有兩種選擇。第一種選擇關閉完成f[i+1][j][0]以後往左走一步,第二種是關閉完f[i+1][j][1]以後長途跋涉走到最左邊。註意第二種並不是來搞笑的,對於f[i+1][j][1]遠小於f[i+1][j][0]時,這就是很有效的。那麽每一次轉移以後需要加上多少的能耗呢?由於f[i+1][j]一定已經是關掉了的,正在消耗的電燈一定是除了[i+1,j]以外的所有電燈,這裏弄一個前綴和搞一下就好了。另外每一秒都會消耗電能,那麽具體消耗了幾秒鐘就是老王這一次走的距離。

那麽對於f[i][j][1]就很簡單了,一種方法f[i][j-1][1]然後往右走一步,或者f[i][j-1][0]然後一路往右走。

f[i][j][0] = Min(f[i][j][0], f[i+1][j][0] + (p[i+1]-p[i]) * (sum[i]+sum[n]-sum[j]));

f[i][j][0] = Min(f[i][j][0], f[i+1][j][1] + (p[j]-p[i]) * (sum[i]+sum[n]-sum[j]));

f[i][j][1] = Min(f[i][j][1], f[i][j-1][1] + (p[j]-p[j-1]) * (sum[i-1]+sum[n]-sum[j-1]));

f[i][j][1] = Min(f[i][j][1], f[i][j-1][0] + (p[j]-p[i]) * (sum[i-1]+sum[n]-sum[j-1]));

代碼註意點:

F數組事先inf,並且預處理f[c][c]為0.

 1 /*This Program is written by QiXingZhi*/
 2 #include <cstdio>
 3 #include <cstring>
 4 #define  N    (55)
 5 #define  ll    long long
 6 #define  INF    (0x7f7f7f7f)
 7 #define  read(x)    x=Rd()
 8 #define  Max(a,b)    (((a) > (b)) ? (a) : (b))
 9 #define  Min(a,b)    (((a) < (b)) ? (a) : (b))
10 #define  FileIn(x)    freopen(x".in","r",stdin)
11 using namespace std;
12 inline int Rd(){
13     char c = getchar(); int x = 0;int w = 1;
14     while(c ^ - && (c < 0 || c > 9)) c=getchar();
15     if(c == -) w = -1, c = getchar();
16     while(c >= 0 && c <= 9) x = (x<<3)+(x<<1)+c-48,c = getchar();
17     return x * w;
18 }
19 int n,c;
20 int p[N],w[N],sum[N];
21 int f[N][N][2];
22 int main(){
23 //    FileIn();
24     read(n),read(c);
25     memset(f,0x3f,sizeof(f));
26     for(int i = 1; i <= n; ++i){
27         read(p[i]),read(w[i]);
28         sum[i] = sum[i-1] + w[i];
29     } 
30     f[c][c][0] = f[c][c][1] = 0;
31     for(int i = c; i > 0; --i){
32         for(int j = c; j <= n; ++j){
33             if(i>c||j<c)continue;
34             f[i][j][0] = Min(f[i][j][0], f[i+1][j][0] + (p[i+1]-p[i]) * (sum[i]+sum[n]-sum[j]));
35             f[i][j][0] = Min(f[i][j][0], f[i+1][j][1] + (p[j]-p[i]) * (sum[i]+sum[n]-sum[j]));
36             f[i][j][1] = Min(f[i][j][1], f[i][j-1][1] + (p[j]-p[j-1]) * (sum[i-1]+sum[n]-sum[j-1]));
37             f[i][j][1] = Min(f[i][j][1], f[i][j-1][0] + (p[j]-p[i]) * (sum[i-1]+sum[n]-sum[j-1]));
38         }
39     }
40     printf("%d", Min(f[1][n][0], f[1][n][1]));
41     return 0;
42 }

2018-05-13 11:35:03

關路燈