题目描述
桐爷人称计科老司机,有天要从深圳跑长途去帝都。桐爷的塞恩车每跑一千米耗油一升。塞恩车的油箱容量为200升。桐爷出发时有100升油,为了造成少耗油的假象到达帝都时至少有100升油。沿途有不少加油站,但是大多都是黑油站,油的价格差别大。桐爷心算了达帝都时的最小花费,请你也计算一下,看你是否能算对,有老司机的潜质。
输入
第一行是深圳到帝都的距离(小于10000千米),接着是不多于100个的黑油站的信息,按离深圳由近到远排列,包括油站到深圳的距离和每升油的价格(不超过2000)。
样例输入
500
100 999
150 888
200 777
300 999
400 1009
450 1019
500 1399
输出
桐爷到帝都的最小花费。如果不能按要求到达,输出Impossible
样例输出
450550
题解
一开始老师就跟我们说了这道题是线性动态规划,想了好久,想动态转移方程,后面发现这道题跟背包有点像。
动态转移方程如下:
dp[i][j] = min( dp[i][ j ] , dp[i-1][j+w-k ]+k*p[i] )这边的动态转移方程含义是:在第i个加油站加完k升油后具有j升油。
其中w为第i个油站和第i-1个油站之间的距离。
这边比较难理解的就是j+w-k,由于我已经说明j表示的是在第i个加油站加完油后的油量。那么j-k就代表到达第i个油站后尚未加油的状态,那么j-k+w就代表在第i-1个油站要出发往第i个油站的状态,也可简单理解为在第i-1个油站。
那么再次强调一下动态转移方程dp[i][j] = min( dp[i][ j ] , dp[i-1][j+w-k ]+k*p[i] )就表示我在第i个油站加油或不加油的最小话费金额。
注意由于油箱容量为200,所以任何时刻油量都不能超过200。
代码实现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 1e9
int len,n=1;
int dp[100+5][200+5];
int dist[100+5];//第i个站的距离
int p[100+5]; //第i个站的价格
int main(){
cin >> len;
for(n = 1; 2==scanf("%d%d",&dist[n],&p[n]);n++);
n--;
//初始状态dp中的元素初始为无穷大
for(int i=0;i<=n;i++)
for(int j=0;j<=200;j++)
dp[i][j]=INF;
/*
出发点时,花费为0,注意出发时不是在第1个油站哦,
出发点的下一个目标点才是油站,
注意,第一个油站的下标值是从1开始的,不是0开始。
*/
dp[0][100]=0;
//DP递推过程
for(int i=1; i<=n; i++){
//第i站与第i-1站的距离差值
int w=dist[i]-dist[i-1];
//j从0-200枚举,j表示在第i个油站出发时油的容量
for(int j=0; j<=200; j++){
//k表示在第i个油站加了多少油
for(int k=0; k<=j; k++)
/*
j+w-k:在第i-1个油站出发的油量且其油量不能多于200
(赛恩车油箱容量为200)
*/
if(j+w-k<=200 ){
//加和不和的话费不同,我们选择话费较小的
dp[i][j]=min(dp[i][j],
dp[i-1][j+w-k]+k*p[i]);
}
}
}
/*
解释一下下面的判断,由于dist数组存储的是深圳到油站的距离,
len表示深圳到北京的距离,
如果深圳到北京的距离-深圳到最后一个油站的距离大于100,那么桐爷就到不了北京了
仔细思考一下,就能想明白的。(因为我们到北京时车里还需要有100升的汽油)
*/
if(len-dist[n]>100
|| dp[n][100+len-dist[n]]==INF)
printf("Impossible\n");
else
//能够到达北京,并且车里有100升油的最小花费。
printf("%d\n",dp[n][100+len-dist[n]]);
return 0;
}