题解:
因为关灯不需要时间,所以老张走过的路灯肯定会被随手关掉。因此,在关完路灯i~j时,老张要么在i处,要么在j处。因此分两种情况:
1、关完后在i处
老张要么是在关完j路灯后再回头关i路灯的,要么是在关完第i+1盏后继续前进关掉i的,在这两种情况中选择耗能最小的。
2、关完后在i处
老张要么是在关完i路灯后再回头关j路灯的,要么是在关完第j-1盏后继续前进关掉j的,在这两种情况中选择耗能最小的。
因此设dp[i][j][0]表示在关完第i盏灯到第j盏灯后停在i处的最小耗电量,dp[i][j][1]表示在关完第i盏灯到第j盏灯后停在j处的最小耗电量,lamp[i].m为第i盏灯的距离,sum[i][j]表示在关完第i盏灯到第j盏灯后每s所耗电量,可得状态转移方程:
dp[i][j][0]=min(dp[i+1][j][0]+(lamp[i+1].m-lamp[i].m)_sum[i+1][j],dp[i+1][j][1]+(lamp[j].m-lamp[i].m)_sum[i+1][j]);
dp[i][j][1]=min(dp[i][j-1][0]+(lamp[j].m-lamp[i].m)_sum[i][j-1],dp[i][j-1][1]+(lamp[j].m-lamp[j-1].m)_sum[i][j-1]);
//My AC Code:
#include <cstdio>
#include <cstring>
using namespace std;
int n,c,nc[55];
int dp[55][55][2];
int qz[55];//前缀和数组
int jsxh(int a,int b,int l,int r){
return (nc[b]-nc[a])*(qz[n]+qz[l]-qz[r-1]);
}
int min(int a,int b){return a>b?b:a;}
//dp[i][j][0]表示i到j的连续区间 的灯全灭了 灭灯的那个人站在i处
//dp[i][j][1]表示i到j的连续区间 的灯全灭了 灭灯的那个人站在j处
//dp[i][j][0] = min(dp[i+1][j][0]+jsxh(i,i+1,i,j+1),dp[i+1][j][1]+jsxh(i,j,i,j+1));
//dp[i][j][0]可以由dp[i+1][j][0]+jsxh(i,i+1,i,j+1)和dp[i+1][j][1]+jsxh(i,j,i,j+1) 较小者决定
//dp[i+1][j][0] 表示i+1到j区间 灯全灭 且 灭灯人在i+1处,所以在i+1处走到i处 也就是左走,i+1的位置减去i的位置再乘上还没拿掉的灯的总功率 dp[i+1][j][1]如此类推
int main(){
scanf("%d%d",&n,&c);
memset(dp,127,sizeof(dp));
//dp[i][j][0] 和 dp[i][j][1] 一开始都是0 这是有问题的 因为 不是每个灯都是起点,所以 这些区间都设为无限大 等前面往后退,更新状态
for(int i=1;i<=n;i++)scanf("%d %d",&nc[i],&qz[i]),qz[i]+=qz[i-1];
dp[c][c][0]=dp[c][c][1]=0;// 根据题目意思 初始化 dp数组 已知部分 即 一开始那个灯 是可以秒关的 不需要任何消耗
//(外循环)一个循环是向右 内循环向左,从左 小范围到右 大范围
//dp要确定 从哪里往哪里推的方向! 和数据的初始化 还有当前状态 是否能获取上一个状态并且从上一个状态 推出当前状态
for(int j=c;j<=n;j++)
for(int i=j-1;i>=1;i--){
dp[i][j][0]=min(dp[i+1][j][0]+jsxh(i,i+1,i,j+1),dp[i+1][j][1]+jsxh(i,j,i,j+1));
//i+1 到j区间 无论灭灯人在i+1处还是在j处,都要向左(i)走
//jsxh(i,i+1,i,j+1) 前面两个参数表示i+1位置走到第i的位置 后面两个参数表示 1-i 的灯都没关 j+1-n的灯都没关 下面同理可得
dp[i][j][1]=min(dp[i][j-1][0]+jsxh(i,j,i-1,j),dp[i][j-1][1]+jsxh(j-1,j,i-1,j));
//至于jsxh函数中的qz[r-1]是因为上面这一句,j还没有到达,所以j位置的灯的功率也算在里面
}
printf("%d",min(dp[1][n][0],dp[1][n][1]));
//最后结果也要进行选择
return 0;
}