NBU 1063 关灯人 DP

Description

某一村庄在一条线路上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少)。老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯。
为了给村里节省电费,老张记录下了每盏路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能够最节省电。他每天都是在天亮时首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯。开始他以为先算一下左边路灯的总功率再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头去关掉另一边的路灯,而事实并非如此,因为在关的过程中适当地调头有可能会更省一些。
现在已知老张走的速度为1m/s,每个路灯的位置(是一个整数,即距路线起点的距离,单位:m)、功率(W),老张关灯所用的时间很短而可以忽略不计。
请你为老张编一程序来安排关灯的顺序,使老张开始关灯时刻算起所有灯消耗电量最少(灯关掉后便不再消耗电了)。


Input

文件第一行是两个数字n(0<n<50,表示路灯的总数)和c(1<=c<=n老张所处位置的路灯号);
接下来n行,每行两个数据,表示第1盏到第n盏路灯的位置和功率。


Output

一个数据,即最少的功率(单位:J,1J=1WS)。


Sample Input


5 3
2 10
3 20
5 20
6 30
8 10


Sample Output


270

 

 

DP三大步:

一、确定数组意义:dp[i][j]保存最远到过i,现在在j的情况下最优的时间花费以及电能消费,其中ij必定分别在起点的两边(可以包括起点)。

二、确定初始状态:dp[起点][起点]耗时耗能都为0,dp[起点][j]、dp[i][起点]自己看着办。

三、确定转移方程:除了第二步初始的以外dp[i][j]只有两种情况转来:

A、从与当前位置相邻的走来。

B、从最远到过的i走来。

那可以根据根据之前的情况分别得出这两种情况下耗时与耗能。

那AB取哪一个好呢?

由于从当前状态出发到末状态的最优方案与我当前的耗能耗时无关(不是说结果与这个无关,是方案),也就是说这个方案是确定了的。那我假设这个方案是由0时刻0耗能开始的,最后的耗能设为X。那如果我取A情况,最后实际耗能就是A.耗能+A.耗时*(A的情况下剩余电灯的功率和)+X。(由这个公式就可看出前面一句的正确性)

那我要比较AB哪个更优就只要比较 耗能+耗时*(当前情况下剩余电灯的功率和)哪个更小就好了。

#include<iostream>
int N,C;
struct Unit{
	int loc,c;
};
struct DP{
	int T,C,n;
	void disp(){
		printf("%d %d %d\n",T,C,n);
	}
	bool friend operator<(DP a,DP b){
		return a.T*a.n+a.C<b.T*b.n+b.C;
	}
};

DP dp[510][510];//[i][j]最远到过i,现在在j
Unit light[510];
int f_min(int x,int y){
	return x<y?x:y;
}
int f_abs(int x){
	return x<0?-x:x;
}
void get_data(){
	C--;
	int i;
	for(i=0;i<N;i++){
		scanf("%d%d",&light[i].loc,&light[i].c);
	}
}
void get_dp(int g,int now){
	if(dp[g][now].T!=-1)return;
	DP A,B;
	if(g<now){
		get_dp(g,now-1);get_dp(now-1,g);
		A.n=B.n=dp[g][now-1].n-light[now].c;
		A.T=dp[g][now-1].T+light[now].loc-light[now-1].loc;
		A.C=dp[g][now-1].C+A.T*light[now].c;
		B.T=dp[now-1][g].T+light[now].loc-light[g].loc;
		B.C=dp[now-1][g].C+B.T*light[now].c;
		if(A<B)dp[g][now]=A;
		else dp[g][now]=B;
	}else{
		get_dp(g,now+1);get_dp(now+1,g);
		A.n=B.n=dp[g][now+1].n-light[now].c;
		A.T=dp[g][now+1].T+light[now+1].loc-light[now].loc;
		A.C=dp[g][now+1].C+A.T*light[now].c;
		B.T=dp[now+1][g].T+light[g].loc-light[now].loc;
		B.C=dp[now+1][g].C+B.T*light[now].c;
		if(A<B)dp[g][now]=A;
		else dp[g][now]=B;
	}
}
void run(){
	int i,j;
	for(i=0;i<N;i++){
		for(j=0;j<N;j++){
			dp[i][j].T=-1;
		}
	}
	dp[C][C].C=0;
	dp[C][C].T=0;
	dp[C][C].n=0;
	for(i=0;i<C;i++)dp[C][C].n+=light[i].c;
	for(i=C+1;i<N;i++)dp[C][C].n+=light[i].c;
	for(i=C+1;i<=N-1;i++){
		dp[C][i].n=dp[C][i-1].n-light[i].c;
		dp[i][C].n=dp[C][i].n;
		dp[C][i].T=light[i].loc-light[C].loc;
		dp[i][C].T=dp[C][i].T<<1;
		dp[C][i].C=dp[C][i-1].C+dp[C][i].T*light[i].c;
		dp[i][C].C=dp[C][i].C;
	}
	for(i=C-1;i>=0;i--){
		dp[C][i].n=dp[C][i+1].n-light[i].c;
		dp[i][C].n=dp[C][i].n;
		dp[C][i].T=light[C].loc-light[i].loc;
		dp[i][C].T=dp[C][i].T<<1;
		dp[C][i].C=dp[C][i+1].C+dp[C][i].T*light[i].c;
		dp[i][C].C=dp[C][i].C;
	}
	get_dp(0,N-1);get_dp(N-1,0);
	printf("%d\n",f_min(dp[0][N-1].C,dp[N-1][0].C));
}
int main(){
	    scanf("%d%d",&N,&C);
		get_data();
		run();
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值