【NOJ1149】【算法实验四】旅游预算

1149.旅游预算

时限:1000ms 内存限制:10000K 总时限:3000ms

描述

一个旅行社需要估算乘汽车从某城市到另一城市的最小费用,沿路有若干加油站,每个加油站收费不一定相同。旅游预算有如下规则: 若油箱的油过半,不停车加油,除非油箱中的油不可支持到下一站;每次加油时都加满;在一个加油站加油时,司机要花费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

#include <iostream>
#include <stdio.h>
#include <float.h>
 
using namespace std;
 
/********输入数据部分*******/
double length;
double capa,mile,cost;
int n;
 
double dis[51];
double price[50];
/****************************/
 
/****算法中用到的其他变量***/
bool isAdd[50];     //在第i个加油站是否加油
 
double min_cost[50];    //备忘录
 
int cnt;
 
double maxdis;      //加满油后能走的最大距离
/****************************/
 
/**********函数声明*********/
double dp(int i);   //返回“从第i个加油站满油出发,到达终点的最小费用”
 
void input();   //输入数据
void init();    //变量初始化
void output(double mincost);   //输出数据
/****************************/
 
int main()
{
    input();    //输入数据
 
    init();     //变量初始化
 
    double mincost=dp(0);  //计算得到最小费用
 
    output(mincost);    //输出数据
 
    return 0;
}
 
void input()    //输入数据
{
    //第一行
    cin>>length;
 
    //第二行
    cin>>capa>>mile>>cost>>n;
 
    //接下去的n行
    for(int i=1; i<=n; i++)   //加油站从1开始编号
    {
        cin>>dis[i]>>price[i];  //输入距离和油价
    }
 
    dis[0]=0;           //假设起点为第0个加油站(实际上没有)
    dis[n+1]=length;    //假设终点为第n+1个加油站(实际上没有)
}
 
void init()     //变量初始化
{
    for(int i=1; i<=n+1; i++)
    {
        isAdd[i]=false; //每个加油站初始化为不加油
        min_cost[i]=DBL_MAX;    //从加油站i到终点的费用设为最大值
    }
    min_cost[0]=DBL_MAX;    //从起点到终点的费用设为最大值
 
    cnt=0;  //需要加油的加油站个数
 
    maxdis=capa*mile;   //加满油后能到达的最大距离
}
 
double dp(int i)    //满油状态从加油站i到终点的最小花费
{
    if(i==n+1)      //到达终点
    {
        return cost;    //算上“在起点加满油的费用”
    }
 
    else if(min_cost[i]!=DBL_MAX)   //备忘录中有记录
    {
        return min_cost[i]; //直接返回
    }
 
    else    //若备忘录中无记录,则需要计算填写
    {
        double fuel_j;      //从加油站i到达加油站j耗费的油量
        double fuel_remain; //从加油站i到达加油站j后,剩下的油量
        double cost_j;      //从加油站i到达加油站j,并在加油站j加满油的费用
        double cost_j_n;    //从加油站i,经过加油站j加油并到达终点的总费用
 
        int min_j=-1;   //记录需要加油的加油站下标
 
        for(int j=i+1; j<=n+1&&(dis[j]-dis[i])<=maxdis; j++)   //遍历从加油站i出发,能够到达的加油站j
        {
            fuel_j=(dis[j]-dis[i])/mile;    //计算从加油站i到达加油站j耗费的油量
 
            fuel_remain=capa-fuel_j;    //计算到达加油站j后剩下的油量
            if(fuel_remain>capa/2       //如果剩下的油超过一半
               &&fuel_remain*mile>=(dis[j+1]-dis[j]))   //而且剩下的油能够到达第j+1个加油站
            {
                continue;   //那么规则不允许加油
            }
 
            if(j==n+1)      //如果能直接到达终点
            {
                cost_j=0;   //到达终点不用加油,也不用算油费了
            }
            else            //否贼就要在加油站j加满油
            {
                cost_j=2;   //给司机买糖吃
                cost_j+=fuel_j*price[j];    //加满油的费用=耗费的油*油价
            }
 
            cost_j_n=cost_j+dp(j);  //从i到达终点的费用=从i到达j的费用+从j到达终点的费用
 
            if(min_cost[i]>cost_j_n)    //遍历j时,取最小的“从i到达终点的费用”
            {
                min_cost[i]=cost_j_n;   //记录从i到终点的最小费用
                min_j=j;    //记录从i到终点的途中曾在加油站j加过油
            }
        }
 
        if(min_j!=-1)   //如果min_j不等于初始值,证明途中加过油
        {
            isAdd[min_j]=true;  //记录从i到终点的途中曾在第min_j个收费站加过油
        }
 
        return min_cost[i]; //返回从i到终点的最小费用
    }
}
 
void output(double mincost)
{
    //输出第一行
    printf("%.2f ",mincost);
 
    //计算并输出加油次数
    for(int i=1; i<=n; i++)
    {
        if(isAdd[i])
        {
            cnt++;
        }
    }
    cout<<cnt<<endl;
 
    //输出第二行
    for(int i=1; i<=n; i++)
    {
        if(isAdd[i])
        {
            cout<<i<<' ';
        }
    }
    cout<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值