洛谷 P1220 关路灯(区间dp,前缀和)

传送门


解题思路

先明确一下题意,c指的是路灯的编号而不是位置。

然后根据贪心,在从点i去关点j的路灯时,所有经过的路灯都会随手关掉(不耗时间),所以我们可以确定,若i点和j点的路灯已经关闭,那么区间i...j的路灯已经全部关闭,而且关完后,最优策略一定是在点i处或者点j处。

这和上一题就很像了,用dp[i][j]表示把区间i...j的路灯全部关闭所有的最小电量。然后dp[i][j][0]表示关完后在i点时的最小电量,dp[i][j][1]表示关完后在j点时的最小电量。

最后想一下动态转移方程,dp[i][j][0]可以从dp[i+1][j][0]和dp[i+1][j][1]转移而来,dp[i][j][1]可以从dp[i][j-1][0]和dp[i][j-1][1]转移而来。

怎样计算呢?很显然是原来的dp值再加上关第i盏灯或第j盏灯要付出的代价。

代价怎么求?

我们很显然能作差求出距离(所用的时间),我们还需要知道到目前为止剩下的还未关的灯的功率和,因为已经关了的灯一定在某一区间内,然后又要求和,所以很自然地就想到了前缀和。

最后的最后,last but not least,重要的事情说三遍:

细节,细节,细节!

AC代码

 1 #include
 2 #include
 3 using namespace std;
 4 int n,a[105],w[105],dp[105][105][2],now,sum[105];
 5 int main()
 6 {
 7     cin>>n>>now;
 8     memset(dp,0x3f,sizeof(dp));
 9     for(int i=1;i<=n;i++){
10         cin>>a[i]>>w[i];
11         sum[i]=sum[i-1]+w[i];
12     }
13     dp[now][now][0]=dp[now][now][1]=0;
14     for(int len=2;len<=n;len++){
15         for(int i=max(1,now-len+1);i<=min(now,n);i++){
16             int j=i+len-1;
17             if(j>n) break;
18             dp[i][j][0]=min(dp[i+1][j][0]+(sum[i]+sum[n]-sum[j])*(a[i+1]-a[i]),dp[i+1][j][1]+(sum[i]+sum[n]-sum[j])*(a[j]-a[i]));
19             dp[i][j][1]=min(dp[i][j-1][0]+(sum[i-1]+sum[n]-sum[j-1])*(a[j]-a[i]),dp[i][j-1][1]+(sum[i-1]+sum[n]-sum[j-1])*(a[j]-a[j-1]));
20         }
21     }
22     cout<1][n][0],dp[1][n][1]);
23     return 0;
24 }

 

你可能感兴趣的:(洛谷 P1220 关路灯(区间dp,前缀和))