动态规划:P1220关路灯 区间DP、前缀和
P1220关路灯
思路与分析:
这题还是区间dp,dp[i][j]代表已经关掉了i->j区间的路灯 消耗的电能,显然这样DP存在3个问题需要我们解决:①在DP的过程中,如何在dp值上面加上每次耗的总电能 ②状态转移时 dp[i][j] 从小区间转移来的时候,与方向有关,就是从小区间的左边和右边转移来的,走的路径是不一样的。③怎么保证从题目给的起点开始关灯
①的解决方法:
我们用一个二维数组sum[i][j]代表已经关掉i 到 j区间的灯 每秒所耗的电能,我们可以利用前缀和数组pre,然后求出0->i-1 r+1->n区间的耗电量,对于其实就是前缀和中pre[n]-(pre[j]-pre[i-1]);大区间减去i->j区间.
②的解决方法
可以学习这一题我的做法:动态规划:P3205[HNOI2010]合唱队 区间DP - 朱朱成 - 博客园 (cnblogs.com),构建dp[][][2],三维的,但是第三维只有两层,0代表上一个关的灯在区间左边,1代表上一个关的灯在区间右边边,我们就很容易得出状态转移方程求解:
③的解决方法
给起点的dp[c][c][0]、dp[c][c][1]初始化为0,其他全部初始化为很大的值,0x7ffffff,这样可以因为我们都是用Min函数,所以可以保证一切区间都是从dp[c][c][0]、dp[c][c][1]推导而来,其他dp[else][else]都被初始化为很大的值,不会成为推出其他大区间的最优解.
注意最后写答案的时候是min(dp[1][n][0],dp[1][n][1]);
完整AC代码:
1 #include<iostream> 2 #include<cmath> 3 #include<algorithm> 4 #include<vector> 5 #include<cstring> 6 #include<string> 7 #include<cstdio> 8 using namespace std; 9 const int maxn = 55; 10 const int inf = 0x7ffffff;//初始化为0x7fffffff会爆Int 因为后面要加东西 所以要小一个f 11 struct lamp 12 { 13 int num, data; 14 } s[maxn]; 15 int dp[maxn][maxn][2]; 16 int sum[maxn][maxn];//表示在关完i到j区间的路灯后每秒损耗的电能 17 int before[maxn];//前缀和数组 18 int read() 19 { 20 int x = 0, f = 1; 21 char ch = getchar(); 22 while (ch > '9' || ch < '0') 23 { 24 if (ch == '-') 25 f = -1; 26 ch = getchar(); 27 } 28 while (ch >= '0' && ch <= '9') 29 { 30 x = (x << 3) + (x << 1) + (ch - '0'); 31 ch = getchar(); 32 } 33 return x * f; 34 } 35 int main() 36 { 37 int n, c; 38 n = read(); 39 c = read(); 40 for (int i = 1; i <= n; ++i)s[i].num = read(), s[i].data = read(); 41 for (int i = 1; i <= n; ++i) 42 for (int j = 1; j <= n; ++j) 43 dp[i][j][0] = dp[i][j][1] = inf;//算最小值 先初始化为一个很大的值 44 for (int i = 1; i <= n; ++i)before[i] = before[i - 1] + s[i].data;//前缀和数组 45 dp[c][c][0] = dp[c][c][1] = 0;//一开始就在c点 所以c点的灯是关的 46 for (int i = 1; i <= n; ++i) 47 for (int j = 1; j <= n; ++j) 48 sum[i][j] = before[n] - (before[j] - before[i - 1]);//表示在关完i到j区间的路灯后每秒损耗的电能 前缀和来算 49 for (int len = 2; len <= n; ++len) 50 { 51 for (int l = 1; l + len - 1 <= n; ++l) 52 { 53 int r = l + len - 1; 54 dp[l][r][0] = min(dp[l + 1][r][0] + sum[l + 1][r] * (s[l + 1].num - s[l].num), dp[l + 1][r][1] + sum[l + 1][r] * (s[r].num - s[l].num)); 55 dp[l][r][1] = min(dp[l][r - 1][0] + sum[l][r - 1] * (s[r].num - s[l].num), dp[l][r - 1][1] + sum[l][r - 1] * (s[r].num - s[r - 1].num)); 56 57 } 58 } 59 cout << min(dp[1][n][0], dp[1][n][1]); 60 return 0; 61 }