0
点赞
收藏
分享

微信扫一扫

【14周-星耀】节约用电 (洛谷P1220 关路灯)


​​关路灯 - 洛谷​​

下面这个题意有误导性没说清楚,最好看洛谷原题。

注意,每天一早进行关灯,然后从早到晚走一天,走到下一个位置,如果这个位置有灯,则第二天早上再关这个灯。第i个灯一天消耗的电量是a[i],这样就好理解了。(其实这种题意都可以抽象出【一天一操作】的模型,注意是早上操作还是晚上操作就好)

【14周-星耀】节约用电 (洛谷P1220 关路灯)_算法

【14周-星耀】节约用电 (洛谷P1220 关路灯)_算法_02

 

【14周-星耀】节约用电 (洛谷P1220 关路灯)_算法_03

 解题报告:

总体思路是先贪心后dp,和之前组内贪心组间dp的套路有异曲同工之妙。

几个关键点:

首先我要关某个灯,肯定是顺手把路过的都关了。

其次整个过程的任意时刻,被关掉的灯一定在一个连续区间内。(即不会有离散点)

到这一步其实就坐实dp了。

所以我站在c,目标是第i个灯,那么肯定[c,i]区间的灯都关掉了。换言之,每一次新关一个灯,我一定站在已关灯区间的某一端,而不是在中间。所以要加一个维度表示在左端点还是右端点,因此三维dp。

这题dp定义状态的时候要注意,dp[i][j]考虑的不是[i][j]的消耗量,而是整个区间的消耗量的最小值。这题与一般的dp不同,不是只考虑完全的子问题,而是把对全局的一些因素也考虑进去了,因为对于这个有题目背景的题,只有这样才可以做决策。

ps:如果我就dp[i][j]代表把[i,j]都关掉,[i,j]这边消耗的电量和,应该也是可以的,因为其实不会有交叉影响?

状态定义:dp[i][j][2]代表 把[i,j]的等刚好关完的那一刻,并且人站在左/右端点时,耗电的最小值。 

AC代码:

#include<iostream>
#include<cstring>

using namespace std;
int dp[55][55][2];// dp[i][j][2] 把[i,j]刚好关完的那一刻,耗电的最小值
int a[55], w[55], sum[55];
int n,c;
int main()
{
cin>>n>>c;
for(int i = 1; i<=n; i++) cin>>a[i]>>w[i], sum[i] = sum[i-1] + w[i];
memset(dp, 0x3f, sizeof(dp));
dp[c][c][0] = dp[c][c][1] = 0;
for(int len = 2; len <= n; len++) {
for(int l = 1; l+len-1<= n; l++) {
int r = l+len-1,t1,t2;
t1 = dp[l+1][r][0] + (a[l+1]-a[l])*(sum[n]-sum[r]+sum[l]);
t2 = dp[l+1][r][1] + (a[r]-a[l])*(sum[n]-sum[r]+sum[l]);
dp[l][r][0] = min(t1, t2);
t1 = dp[l][r-1][1] + (a[r]-a[r-1])*(sum[n]-sum[r-1]+sum[l-1]);
t2 = dp[l][r-1][0] + (a[r]-a[l])*(sum[n]-sum[r-1]+sum[l-1]);
dp[l][r][1] = min(t1, t2);
}
}
printf("%d\n",min(dp[1][n][0], dp[1][n][1]));

return 0;
}

1、如何想到第三维度的状态设计?如果设计的子问题无法满足当前问题的结构(不是最优子问题),分段提炼一下,思考当前可以求得什么最优值,把和子问题有关的那一部分划在子问题内部,重新写状态。
2、最优子问题并不是要局限于仅仅在那一部分操作,是可以包含那一部分对全局的影响的,只要最后的答案是对的,状态设计确实是可以有很多种。

举报

相关推荐

0 条评论