NYOJ 304 节能 -- 区间dp

/*
	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]

*/


#pragma comment(linker, "/stack:64000000")

#include <queue>
#include <cmath>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define CLR(c,v) memset(c,v,sizeof(c))

template <typename _T>
_T Abs(_T a ){
	return (a>0)?(a):(-a);
}
template <typename _T>
_T Max(_T a , _T b){
	return (a>b)?(a):(b);
}
template <typename _T>
_T Max(_T a , _T b, _T c){
	return (a>Max(b,c))?(a):(Max(b,c));
}
template <typename _T>
_T Min(_T a , _T b){
	return (a<b)?(a):(b);
}
template <typename _T>
_T Min(_T a , _T b, _T c){
	return (a<Min(b,c))?(a):(Min(b,c));
}

const int inf    = -(1<<30);
const int INF    =  (1<<30);
const double eps =  1e-8;
const int M      =  1e3 +10;

int d[M]; // 前缀和 路灯位置
int dp[M][M][2]; // dp[i][j][0]   [i,j]区间 karl机器人在左端点 的最小消耗   dp[i][j][1] 为在右端点 
int c[M]; // 前缀和 每个路灯的消耗 每秒多少w

int main()
{
	freopen("in.txt","r",stdin);
	int n;
	while( cin >> n ){
		int s; cin >> s;
		int sumW = 0; // 所有路灯每秒总消耗
		//CLR(dp,0);CLR(c,0);CLR(d,0);
		d[0] = c[0] = 0;
		dp[s][s][0] = dp[s][s][1] = 0;
		for(int i = 1 ; i <= n ; i++){
			scanf("%d %d",&d[i],&c[i]); // d已经是前缀和了
			sumW += c[i];
			c[i] += c[i-1]; //   前缀和
		}
		for(int i = s-1 ; i >= 1 ; i--){ // 初始化前半
			dp[i][s][0] = dp[i+1][s][0] + (d[i+1] - d[i])*(sumW - c[s] + c[i+1-1]);
			dp[i][s][1] = dp[ i ][s][0] + (d[ s ] - d[i])*(sumW - c[s] + c[ i -1]);
   		}
		for(int j = s+1 ; j <= n ; j++){ // 初始化后半
			dp[s][j][1] = dp[s][j-1][1] + (d[j] - d[j-1])*(sumW - c[j-1] + c[s-1]);
			dp[s][j][0] = dp[s][ j ][1] + (d[j] - d[ s ])*(sumW - c[ j ] + c[s-1]);
            
		}
		for(int i = s-1 ; i >= 1 ; i--){ // 中间到左边
			for(int j = s+1 ; j <= n ; j++) {// 中间到右边
				
				dp[i][j][0] = Min(
					dp[i+1][j][0] + (d[i+1] - d[i])*(sumW - c[j] + c[i+1-1]) , 
					dp[i+1][j][1] + (d[ j ] - d[i])*(sumW - c[j] + c[i+1-1]) );                

				dp[i][j][1] = Min(
					dp[i][j-1][0] + (d[j] - d[ i ])*(sumW - c[j-1] + c[i-1]) ,  // 这里减去的是已经关闭的灯消耗的电能。
					dp[i][j-1][1] + (d[j] - d[j-1])*(sumW - c[j-1] + c[i-1]) );
				 
			}
		}
		int ans = Min(dp[1][n][0] , dp[1][n][1]);
		printf("%d\n" , ans);
	}
	return 0;
}


/*
5
3
0 5
2 1
3 2
6 10
10 4
=158
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值