动态规划: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 }

 

 

posted @ 2022-04-21 10:47  朱朱成  阅读(64)  评论(0编辑  收藏  举报