题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=304
一个区间里面有很多不重复的灯,机器人从其中一个灯开始关灯。给出灯和原点的距离 和 灯的功率,问机器人从开始关灯到关灯结束总共浪费的电能机器人每秒移动一米。因为各个灯消耗的电能不一样,所以机器人的关灯的选择有一定策略思路。
区间dp
dp[i][j][0] [i,j]之间的灯关闭了,机器人在第i个灯,浪费的最小电能
dp[i][j][1] [i,j]之间的灯关闭了,机器人在第j个灯,浪费的最小电能
显然想要计算当前区间[i,j]之间的最小电能,可以由 区间[i+1,j]或 区间[i,j-1]推算
对于这两个区间,可以从任意一个区间的左端点或者右端点到达当前区间
dp[i,j][0] = min(dp[i+1,j][0] + [i+1,j]区间外浪费的电能, dp[i+1,j][1] + [i+1,j]区间外浪费的电能);
dp[i,j][1] = min(dp[i,j-1][0] + [i,j-1]区间外浪费的电能, dp[i,j-1][1] + [i,j-1]区间外浪费的电能);
另外:由前缀和[0,i],[0,j]求任意区间和[i,j]的时候 [i,j] = [j,0] - [i-1,0]
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int maxn = 1000 + 10;
int dp[maxn][maxn][2];// dp[i][j][0] [i,j]区间 karl机器人在左端点 的最小消耗 dp[i][j][1] 为在右端点
int d[maxn], sv[maxn];
int main()
{
int n, k, a, b;
// freopen("Input.txt", "r", stdin);
// freopen("O.txt", "w", stdout);
while(~scanf("%d", &n))
{
memset(d, 0, sizeof(d));
memset(sv, 0, sizeof(sv));
memset(dp, 0, sizeof(dp));
scanf("%d", &k);
int sumv = 0;// 所有路灯每秒总消耗
for(int i = 1; i <= n; i++)
{
scanf("%d%d", &d[i], &b);
sumv += b;
sv[i] = b + sv[i-1];
}
for(int i = k-1; i > 0; i--)// 初始化前半
{
dp[i][k][0] = dp[i+1][k][0] + (sumv-sv[k]+sv[i+1-1])*(d[i+1]-d[i]);
dp[i][k][1] = dp[i][k][0] + (sumv-sv[k]+sv[i-1])*(d[k]-d[i]);
}
for(int i = k+1; i <= n; i++)// 初始化后半
{
dp[k][i][1] = dp[k][i-1][1] + (sumv-sv[i-1]+sv[k-1])*(d[i]-d[i-1]);
dp[k][i][0] = dp[k][i][1] + (sumv-sv[i]+sv[k-1])*(d[i]-d[k]);
}
for(int i = k-1; i > 0; i--)// 中间到左边
for(int j = k+1; j <= n; j++)// 中间到右边
{
dp[i][j][0] = min(dp[i+1][j][0] + (sumv-sv[j]+sv[i+1-1])*(d[i+1]-d[i]),
dp[i+1][j][1] + (sumv-sv[j]+sv[i+1-1])*(d[j]-d[i]));
dp[i][j][1] = min(dp[i][j-1][0] + (sumv-sv[j-1]+sv[i-1])*(d[j]-d[i]),
dp[i][j-1][1] + (sumv-sv[j-1]+sv[i-1])*(d[j]-d[j-1]));
}
int ans = min(dp[1][n][0], dp[1][n][1]);
printf("%d\n", ans);
}
return 0;
}