NOJ-1149-旅游预算

http://phoenix-zh.cn/2020/11/04/NOJ-旅游预算/

旅游预算

描述

一个旅行社需要估算乘汽车从某城市到另一城市的最小费用,沿路有若干加油站,每个加油站收费不一定相同。旅游预算有如下规则: 若油箱的油过半,不停车加油,除非油箱中的油不可支持到下一站;每次加油时都加满;在一个加油站加油时,司机要花费2元买东西吃;司机不必为其他意外情况而准备额外的油;汽车开出时在起点加满油箱;计算精确到分(1元=100分)。编写程序估计实际行驶在某路线所需的最小费用。

输入

第一行为起点到终点的距离(实数) 第二行为三个实数,后跟一个整数,每两个数据间用一个空格隔开。其中第一个数为汽车油箱的容量(升),第二个数是每升汽油行驶的公里数,第三个数是在起点加满油箱的费用(精确到分),第四个数是加油站的数量。(〈=50)。接下去的每行包括两个实数,每个数据之间用一个空格分隔,其中第一个数是该加油站离起点的距离,第二个数是该加油站每升汽油的价格(元/升)。加油站按它们与起点的距离升序排列。所有的输入都有一定有解。

输出

共两行,每行都有换行 第一行为一个实数和一个整数,实数为旅行的最小费用,以元为单位,精确到分,整数表示途中加油的站的N。第二行是N个整数,表示N个加油的站的编号,按升序排列。数据间用一个空格分隔,最后一个数据后也输出空格,此外没有多余的空格。

输入样例

516.3 15.7 22.1 20.87 3 125.4 1.259 297.9 1.129 345.2 0.999

输出样例

38.09 1

2

思路

由题意可知:如果在第i个加油站要加油,必须保证到达第i+1个加油站的时候油箱的油少于一半。并且一旦加油,油箱的油就加满了。dp[i]表示到达第i个加油站的最小耗费,初始化为1e9(无穷大)。dp[0]: 起点加满油箱的费用 。a[i].d表示第i个加油站距离起点的距离,a[i].w表示第i个加油站加油的单价费用。枚举当前到达的加油站i(i from 1 to n),然后再枚举上一次加油的加油站j(j from 0 to i-1,0就是起点)。if(a[i].d-a[j].d>road||(a[i].d-a[j].d) * 2<road)如果无法从j到达i或者从j到达i油箱油量过半都无法考虑,其余的情况dp[i]=min(dp[i],dp[j]+ned * a[i].w+2),因为最后答案要输出在哪些加油站加了油,所以需要记录上一次加油的加油站pre[i]=j;最后再看从哪个加油站i到终点代价最小(i from 0 to n),必须保证油量可以从i到终点,ans为最终答案,ans=min(ans,dp[i]),并且记录最后一个加油站last。由last为终点前的加油站,进行回溯查询加过油的加油站。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1000+50;
struct Node{
    double w,d;
}a[maxn]; 
double s,v,p,dp[maxn];int n,pre[maxn],t[maxn];
double min(double x,double y){
    if(x<=y)return x;
    return y;
}
int main(){
    int i,j;
    cin>>s>>v>>p>>dp[0]>>n;
    for(i=1;i<=n;i++)cin>>a[i].d>>a[i].w,dp[i]=1e9;
    double road=v*p;
    for(i=1;i<=n;i++){
        for(j=0;j<=i-1;j++){
            if(a[i].d-a[j].d>road||(a[i].d-a[j].d)*2<road)continue;//注意油量超过一半并且可以到达下一个点不加油! 
            double ned=(a[i].d-a[j].d)/p;
            if(dp[i]>dp[j]+ned*a[i].w+2){
                dp[i]=dp[j]+ned*a[i].w+2;
                pre[i]=j;
            }
        }
    }
    double ans=0x3f3f3f3f;int last=0,tot=0;
    for(i=0;i<=n;i++){
        if(road<s-a[i].d)continue;
        if(ans>dp[i]){
            ans=dp[i];
            last=i;
        }
    }
    while(last!=0){
        t[++tot]=last;
        last=pre[last];
    }
    printf("%.2lf %d\n",ans,tot);
    for(i=tot;i>=1;i--)cout<<t[i]<<' ';cout<<endl;
    return 0;
}
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Phoenix_ZengHao

创作不易,能否打赏一瓶饮料?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值