首页 > 试题广场 >

To Fill or Not to Fill

[编程题]To Fill or Not to Fill
  • 热度指数:8869 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
With highways available, driving a car from Hangzhou to any other city is easy. But since the tank capacity of a car is limited, we have to find gas stations on the way from time to time. Different gas station may give different price. You are asked to carefully design the cheapest route to go.

输入描述:
For each case, the first line contains 4 positive numbers: Cmax (<= 100), the maximum capacity of the tank; D (<=30000), the distance between Hangzhou and the destination city; Davg (<=20), the average distance per unit gas that the car can run; and N (<= 500), the total number of gas stations. Then N lines follow, each contains a pair of non-negative numbers: Pi, the unit gas price, and Di (<=D), the distance between this station and Hangzhou, for i=1,...N. All the numbers in a line are separated by a space.


输出描述:
For each test case, print the cheapest price in a line, accurate up to 2 decimal places. It is assumed that the tank is empty at the beginning. If it is impossible to reach the destination, print "The maximum travel distance = X" where X is the maximum possible distance the car can run, accurate up to 2 decimal places.
示例1

输入

50 1300 12 8
6.00 1250
7.00 600
7.00 150
7.10 0
7.20 200
7.50 400
7.30 1000
6.85 300
50 1300 12 2
7.10 0
7.00 600

输出

749.17
The maximum travel distance = 1200.00
  • 输入:邮箱容量Cmax_终点距离D_每升油能跑Davg_加油站数量N
  •             N组{油价Pi_加油站距起点Di}
  • 输出:1.最低花费cost(能跑到终点)
  •             2.“The maximum travel distance = xx.xx”(跑不到终点)
  • 思路:1.准备阶段------------------
  •     数据结构:struct Station{    double price, distance; }st[MAXN];//尽量用浮点型,防止被坑
  •     变量:将终点当作最后一站:st[N].distance = D; st[N].price = 0.0;
  •                 满油状态能跑最远距离:maxRun = Cmax * Davg
  •                 全程花费:cost = 0.0
  •                 到达某一站时所剩油量:nowGas = 0.0
  •                 从 i 站到 j 站所需油量:needGas = (Dj - Di)/Davg
  •                 实际加油量:addGas = needGas - nowGas
  •             2.读取处理数据--------------
  •             读取全部数据,并以路程从小到大排序
  •             3.最优策略--------------------
  •             判断在i号加油站时,是否能以满油状态可达到i+k号站?
  •             若k = 0 则无法到达下一站,此次航程最多为Di + maxRun【输出1】
  •             否则:
  •                     若能找到比i号站油价更低的最近加油站j:needGas = (Dj-Di)/Davg
  •                     (抓紧去那个便宜的地方加油)
  •                     |    倘若当前油量nowGas >= needGas 时,说明剩余油量充足,不需要加油;
  •                     |    |        到达j站后,剩余油量nowGas = nowGas - needGas
  •                     |    但油不够时,需要加油addGas = needGas - nowGas;到达j站后,nowGas  = 0.0;
  •                     |            且花费了 cost += addGas * Pi;
  •                     但找不到的话,就退而求其次找个油价最低的:needGas = (Dj-Di)/Davg
  •                    (虽然比i站高点,但还能接受,所以先在i站加满油,这样才不吃亏)
  •                             直接加满油 addGas = Cmax - nowGas; 花了:cost +=addGas * Pi
  •                             到达j站后,nowGas = Cmax - needGas;
  •             最后令 i = j,即以j站开始,继续循环,直到终点站。
  •             4.输出----------------cost
  • 代码:
  • #include "stdafx.h"
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int MAXN = 510;
    struct station{
        double price, distance;
    }st[MAXN];
    bool cmp(struct station a, struct station b){
        return a.distance < b.distance;
    }
    int main(){
        double Cmax, D, Davg;    int N;
        scanf("%lf%lf%lf%d", &Cmax, &D, &Davg, &N);
        for(int n = 0; n < N; ++n){
            scanf("%lf%lf", &st[n].price, &st[n].distance);
        }
        st[N].price = 0.0;st[N].distance = D;
        double maxRun = Cmax * Davg, cost = 0.0, nowGas = 0.0, addGas = 0.0, needGas = 0.0;
        sort(st, st + N, cmp);
        if(st[0].distance != 0){printf("The maximum travel distance = 0.00");return 0;}
        int i = 0;
        while(i < N){
            int k = 0;
            while(i+k <= N && st[i+k].distance - st[i].distance <= maxRun){
                k++;
            }
            k--;
            if(k <= 0){printf("The maximum travel distance = %.2f\n", st[i].distance + maxRun); return 0;}
            int j;
            for(j = i; j <= i + k; ++j){
                if(st[j].price < st[i].price)break;
            }
            if(j <= i + k){
                needGas = (st[j].distance - st[i].distance)/Davg;
                if(nowGas >= needGas)nowGas -= needGas;
                else{addGas = needGas - nowGas; nowGas = 0.0; cost += addGas * st[i].price;}
            }else{
                j = i + 1;
                for(int tmp = i + 1; tmp <= i + k; ++tmp)
                    if(st[tmp].price < st[j].price)j = tmp;
                needGas = (st[j].distance - st[i].distance)/Davg;
                addGas = Cmax - nowGas;
                nowGas = Cmax - needGas;
                cost += addGas * st[i].price;
            }
            i = j;
        }
        printf("%.2f\n", cost);
        return 0;
    }

发表于 2018-07-29 10:58:55 回复(2)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct gas_stations
{
    double pi;
    int di;
};
bool compare(gas_stations g1, gas_stations g2)
{
    return g1.pi < g2.pi;
}
int main()
{
    int C, D, Davg, N;
    double cost;
    while (cin>>C>>D>>Davg>>N)
    {
        gas_stations* G = new gas_stations[N];
        for (int i = 0; i < N; i++)//输入加油站位置、价格
            cin >> G[i].pi >> G[i].di;

        sort(G, G + N,compare);//按价格排序

        cost = 0;//记录花费
        int max = C * Davg, need, dis;
        int* flag = new int[D+10];
        memset(flag, 0, (D + 10) * sizeof(int));
        //关键代码
        for (int i = 0; i < N; i++)
        { //如果到终点距离<max,只需加上足够走到终点的油
            need = (G[i].di + max < D ? max : D - G[i].di);    
            dis = 0;    //记录有多长距离需要该加油站的油
            for (int j = G[i].di; j < G[i].di + need; j++)
            {
                if (flag[j] == 0)
                {
                    flag[j] = 1;
                    dis++;
                }
            }
            cost += dis / (Davg * 1.0) * G[i].pi; //加上在该加油站的花销
        }
        //输出
        cout.precision(2);
        int i;
        for (i = 0; i < D; i++)
        {//有的路程没有被覆盖到说明走不到这里
            if (flag[i] == 0)
            {
                cout << "The maximum travel distance = " <<fixed<< (double)i << endl;
                break;
            }
        }
        if (i == D)//到达目的地
            cout <<fixed<< cost << endl;
        delete[] G, flag;
    }
    return 0;
}

参考高赞回答,C++重写了一下,注释、结构应该都比较清晰
编辑于 2020-06-06 21:52:19 回复(0)
三个解题思路,都没对贪心选择性和最优子结构进行证明。不敢用啊,这题先放着了。
发表于 2020-04-08 20:28:51 回复(0)
#define _CRT_SECURE_NO_WARNINGS
(842)#include <algorithm>
using namespace std;

const int MAXN = 500 + 10;

struct Station_t {
	double price;
	double dest;
	double cov;//最远能覆盖到的坐标点
	bool operator<(const Station_t x) {
		return dest < x.dest;
	}
};
Station_t gStation[MAXN];

int main() {
	int capacity, dest, davg, n;
begin:
	while (scanf("%d%d%d%d", &capacity, &dest, &davg, &n) != EOF) {
		double eDest = (double)capacity * davg;//eDest表示每次油箱装满能走得最远距离
		for (int i = 0; i < n; i++) {
			scanf("%lf%lf", &gStation[i].price, &gStation[i].dest);
			gStation[i].cov = gStation[i].dest + eDest;
		}
		gStation[n].dest = dest;
		gStation[n].price = 0;
		sort(gStation, gStation + n);
		for (int i = 0; i < n; i++) {//判断最终能否到达加油站
			if (gStation[i].cov < gStation[i + 1].dest) {
				printf("The maximum travel distance = %.2lf\n", gStation[i].cov);
				goto begin;
			}
		}
		int index = 0, nextIndex = 0;//index表示当前所在加油站的标识
		double cost = 0, curMass = 0, needMass;//成本、当前油量、到达下一站需要的油量
		while (index < n) {
			double minPrice = gStation[index].price;
			nextIndex = index;
			//找油箱加满后能够行驶的距离范围内比当前加油站油价便宜的最近的加油站
			for (int i = index + 1; i <= n && gStation[i].dest <= gStation[index].cov; i++) {
				if (gStation[i].price <= minPrice) {
					minPrice = gStation[i].price;
					nextIndex = i;
					break;
				}
			}
			//如果找到了可行驶距离范围内比当前所在加油站拥有更低油价的加油站,那么只需要使油量够行驶至此加油站即可
			if (nextIndex != index) {//找到了更便宜的油站
				needMass = (gStation[nextIndex].dest - gStation[index].dest) / davg;//行驶到此站需要的油量
				if (curMass < needMass) {//查看油量是否够用,不够用则加油至刚好能到这一站
					cost += (needMass - curMass) * gStation[index].price;
					curMass = 0;//到达下一站,油量刚好归零
				}
				else {//油量够用,不用再额外加油
					curMass -= needMass;
				}
				index = nextIndex;
			}
			else {//没有找到价格更低的加油站,把油加满,走到下个加油站
				cost += (capacity - curMass) * gStation[index].price;
				curMass = capacity;
				needMass = (gStation[index + 1].dest - gStation[index].dest) / davg;
				curMass -= needMass;
				index++;
			}
		}
		printf("%.2lf\n", cost);
	}
	return 0;
}

编辑于 2020-03-14 20:49:31 回复(1)
基本思路:

将终点看成一个天价加油站,设加满油箱能跑的最大距离dmax,寻找dmax以内(0<距离≤dmax)最便宜的加油站(cheapest)比当前加油站便宜且距离最近的那一个加油站(cheaper);若不存在cheapest,说明dmax以内不存在加油站(包括终点),此时只要输出最大行驶距离,即当前加油站位置+dmax;若存在cheapest且存在cheaper,那我们就开到cheaper(不用加满油,刚好能开到cheaper就行),若存在cheapest但没有cheaper,这时候就要看能不能一口气开到终点,能开到终点直接开到终点,不能开到终点就加满油开到cheapest(由于是加满油,开到cheapest油箱还有油,为了不用计算剩油量,可以用计算差价来等效)。

刚开始的算法用了排序,稀里糊涂就通过了,然后又尝试了不用排序,结果就遇到了很多坑,比如相同位置不止一个加油站(包括起点),这时就要找到那个最便宜的,有些测试点起点加油站最便宜的价格居然大于1000000000!

以下给出不用排序的算法:
#include <stdio.h>
int main() {
    double cmax,d,davg;
    int n;
    while(scanf("%lf%lf%lf%d",&cmax,&d,&davg,&n)!=EOF) {
        double price[1000];
        double dist[1000];
        double dmax=cmax*davg;
        double gross=0;
        int now=n;
        price[n]=2000000000;
        price[n+1]=2000000001;
        dist[n]=d;
        dist[n+1]=d+dmax;
        for(int i=0; i<n; i++) {
            scanf("%lf%lf",&price[i],&dist[i]);
            if(dist[i]==0 && price[i]<price[now])    now=i;
        }
        while(now!=n) {
            int cheapest=n+1;
            int cheaper=n+1;
            for(int i=0; i<=n; i++) {
                if(dist[i]>dist[now] && dist[i]-dist[now]<=dmax && price[i]<price[cheapest])    cheapest=i;
                if(dist[i]>dist[now] && dist[i]-dist[now]<=dmax && ((price[i]<price[now] && dist[i]<dist[cheaper]) || (dist[i]==dist[cheaper] && price[i]<price[cheaper])))    cheaper=i;
            }
            if(cheapest==n+1)    break;
            else if(cheaper!=n+1) {
                gross+=price[now]*(dist[cheaper]-dist[now])/davg;
                now=cheaper;
            } else if(dist[now]+dmax>=d) {
                gross+=price[now]*(d-dist[now])/davg;
                now=n;
            } else {
                gross+=price[now]*(dist[cheapest]-dist[now])/davg-(price[cheapest]-price[now])*(dist[now]+dmax-dist[cheapest])/davg;
                now=cheapest;
            }
        }
        if(now==n)    printf("%.2lf\n",gross);
        else    printf("The maximum travel distance = %.2lf\n",dist[now]+dmax);
    }
    return 0;
}

编辑于 2020-03-05 16:10:42 回复(0)
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct station{
    double p;//单位油价格(不同)
    double d;//本站和杭州之间的距离
};
bool cmp(station x,station y){
    if(x.d==y.d)//距离相同比价格
        return x.p<y.p;
    else
        return x.d<y.d;
}
int main(){//不要用float,全部double
    double Cmax;//油箱最大容量
    double D;//杭州和目的城市之间的距离
    double Davg;//单位***驶里程(相同)
    int n;//加油站的总数
    while(cin>>Cmax>>D>>Davg>>n){
        station sta[n+1];
        for(int i=0;i<n;i++)
            cin>>sta[i].p>>sta[i].d;
        sta[n].p=0,sta[n].d=D;//增加终点站
        sort(sta,sta+n,cmp);
        double cap=0;//剩余油量
        double x=0;//行驶总里程
        double price=0;//总油费
        double Dmax=Cmax*Davg;//满油最大里程
        for(int i=0;i<n;i++){
            if(Dmax<(sta[i+1].d-sta[i].d)){//加满油也到不了下一站
                printf("The maximum travel distance = %.2f\n",x+Dmax);//输出最大行驶历程
                break;
            }
            int cheapest=i+1;//标记油价最便宜的站
            int nearest=i+1;//标记便宜且距离最近的站 
            int pmin=sta[i+1].p;//标记前方最低油价 
            for(int j=i+1;Dmax>=(sta[j].d-sta[i].d)&&j<=n;j++){//在所能到达的所有加油站中(包括终点站) 
                if(sta[j].p<=pmin){//寻找油价最便宜的站
                    cheapest=j;
                    pmin=sta[j].p;
                }
            }
            for(int j=i+1;Dmax>=(sta[j].d-sta[i].d)&&j<=n;j++){//在所能到达的所有加油站中(包括终点站) 
                if(sta[j].p<=sta[i].p){//寻找便宜且距离最近的站
                    nearest=j;
                    break;
                }
            }
            if(sta[i].p<pmin){//当前站油价最便宜,加满油去下一个油价最便宜的站 
                price=price+(Cmax-cap)*sta[i].p;//油费++
                cap=Cmax;//加满
                x=x+sta[cheapest].d-sta[i].d;//里程++
                cap=cap-(sta[cheapest].d-sta[i].d)/Davg;//油量--
                i=cheapest-1;//i++-->cheapest
            }
            else{//前面有油价小于或等于当前油价的站,视情况加油去距离最近的便宜站 
                if(cap*Davg<(sta[nearest].d-sta[i].d)){//如果剩余油到不了最近便宜站
                    price=price+((sta[nearest].d-sta[i].d)/Davg-cap)*sta[i].p;//油费++
                    cap=(sta[nearest].d-sta[i].d)/Davg;//加刚好能到最近便宜站的油
                }
                x=x+sta[nearest].d-sta[i].d;//里程++
                cap=cap-(sta[nearest].d-sta[i].d)/Davg;//油量--
                i=nearest-1;//i++-->nearest
            }
            
        }
        if(x==D)//到达终点站
            printf("%.2f\n",price);
    }
    return 0;
}

编辑于 2020-02-28 15:58:45 回复(1)
/*
核心思路:利用贪心策略,从最便宜的加油站开始,每个加油站加的油最多能走cmax*davg路程.
利用一个30000大小的flag数组记录是否有重合区域
*/
#include<cstdio>
#include<algorithm>
using namespace std;

struct sta{
    double pi;
    int di;
    bool operator < (const sta& b) const{
        return pi<b.pi;
    }
}a[501];

int main(){
    int cmax, d, davg, n;
    double sum;
    while(scanf("%d %d %d %d", &cmax, &d, &davg, &n)!=EOF){
        for(int i=0; i<n; i++) scanf("%lf %d", &a[i].pi, &a[i].di);
        sort(a, a+n);
        //
        sum = 0;//记录花费
        int flag[30001]={0}, max = cmax*davg, tmp, cnt;
        for(int i=0; i<n; i++){
            tmp = (a[i].di+max<d?max:d-a[i].di);    //如果到终点距离<max,只需加上足够走到终点的油
            cnt = 0;    //记录有多长距离需要该加油站的油
            for(int j=a[i].di; j<a[i].di+tmp; j++){
                if(flag[j]==0){
                    flag[j] = 1;
                    cnt++;
                }
            }
            sum += cnt/(davg*1.0)*a[i].pi;    //加上在该加油站的花销
        }
        //check
        int i;
        for(i=0; i<d; i++){
            //有的路程没有被覆盖到说明走不到这里
            if(flag[i]==0){
                printf("The maximum travel distance = %.2lf\n", (double)i);
                break;
            }
        }
        if(i==d){
            printf("%.2lf\n",sum);
        }
    }
    return 0;
}

编辑于 2019-08-24 17:29:53 回复(15)
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef struct station{
    double price;
    double dis;
    bool operator <(const station &A) const {
        return dis<A.dis;
    }
}station;
int main(){
    station buf[501];
    int i,j,n;
    double cmax,d,davg,maxdis,ans;
    while(scanf("%lf%lf%lf%d",&cmax,&d,&davg,&n)!=EOF){
        ans=0;
        maxdis=cmax*davg;
        for(i=0;i<n;i++)
            scanf("%lf%lf",&buf[i].price,&buf[i].dis);
        buf[n].dis=d;
        sort(buf,buf+n);
        if(buf[0].dis!=0){
            printf("The maximum travel distance = 0.00\n");
            continue;
        }
        double curpos=0;
        double curoil=0;
        for(i=0;i<n;i++){
            curpos=buf[i].dis;
            if(curpos+maxdis<buf[i+1].dis){
                printf("The maximum travel distance = %.2lf\n",curpos+maxdis);
                break;
            }
            if(i>0)
                curoil-=(buf[i].dis-buf[i-1].dis)/davg;
            double curdis=0;
            bool ischeap=false;
            for(j=i+1;j<n;j++){
                curdis=buf[j].dis-buf[i].dis;
                if(curdis>maxdis) break;
                if(buf[j].price<buf[i].price){
                    double temp=(buf[j].dis-buf[i].dis)/davg;
                    if(temp>curoil){
                        ans+=(temp-curoil)*buf[i].price;
                        curoil=temp;
                    }
                    ischeap=true;
                    break;
                }
            }
            if(!ischeap){
                if(curpos+maxdis>=d){
                    ans+=((d-curpos)/davg-curoil)*buf[i].price;
                    printf("%.2lf\n",ans);
                    break;
                }
                else{
                    ans+=(cmax-curoil)*buf[i].price;
                    curoil=cmax;
                }
            }
        }
    }
    return 0;
} 

在网上看了一些思路才会做这题,利用贪心算法,每次走一个加油站,记录当前的距离和油量,
然后查看最大距离以内的加油站有没有比当前站便宜的,若有,则查看油量,
把油量加到足够到达便宜的站,若没有,则看油量够不够到终点,不够则把油加满。
在到达没站的过程中若油量不够到达最近的一站,则输出最大距离。
特殊情况:没有距离为0的加油站。
结果实现过程中没想到漏掉一个换行符和一个break弄了我很长时间。
给的测点又看不完全。心塞。。。

编辑于 2018-01-06 23:47:39 回复(3)
/*
    说下思路,这题写了我一天
    贪心的思想,我们将终点看做一个价格为0,距离为最大值的车站
    读入所有车站,按照距离从小到大排序,如果距离相同,按照价格从小到大,因为距离相同的油站我们取最便宜的
    然后默认我们从起始的位置,也就是0起点的油站开始走
    特殊情况:
        如果起始点没有油站,那么车无法走,直接认为无解。

    从当前车站往后搜寻比当前油站价格便宜的。
        1.如果有比当前价格便宜的,那么加油到能够开到这个油站,然后继续寻找下一个更便宜的油站
        2.如果没有,那么记录能够到达的油站中相对价格最低的,认为从当前油站加满油直接开到这个油站
        这个很重要,就是因为这个问题卡了我一整天
        3.如果没有找到比当前价格便宜的油站并且也没找到相对价格便宜的油站,那就退出,用当前油站的距离加上
        满油能走的最大距离,输出
    
*/
#include <bits/stdc++.h>
using namespace std;
struct station{
	double price,dis;
};
bool cmp(station a,station b){
	if(a.dis==b.dis){
		return a.price<b.price;
	}else{
		return a.dis < b.dis;
	}
}

const double inf = 999999999;

int main(){
	double cmax,d,davg;
	int n;
	cin>>cmax>>d>>davg>>n;
	station sta[n+1];
	sta[0].price = 0;sta[0].dis = d;
	for(int i=1;i<=n;i++){
		cin>>sta[i].price>>sta[i].dis;
	}
	sort(sta,sta+n+1,cmp);
	double nowprice = 0.0,nowdis = 0.0;
	double totalprice = 0.0,leftdis = 0.0; // leftdis 用于表示当前最便宜的油还能走多久 
	if(sta[0].dis!=0){
		cout<<"The maximum travel distance = 0.0";
		return 0;
	}else{
		nowprice = sta[0].price;
	}
	while(nowdis!=d){
		double maxdis = cmax*davg+nowdis; // 当前满油能走多远  
		double minprice = inf;
		double minpriceDis = 0.0;
		int flag = 0;
		for(int i=1;i<=n&&sta[i].dis<=maxdis;i++){
			if(sta[i].dis<=nowdis){ // 若油站已经走过 
				continue; 
			}else{
				if(sta[i].price<nowprice){ // 找到比当前价格便宜的油站 
					flag = 1;
					totalprice += (sta[i].dis-nowdis-leftdis)/davg * nowprice;
					leftdis = 0.0;
					nowdis = sta[i].dis;
					nowprice = sta[i].price;
					break;
				}else if(sta[i].price<minprice){ // 记录当前可以到达的价格相对较小的油站 
					minprice = sta[i].price;
					minpriceDis = sta[i].dis;
				}
			} 
		}
		if(flag == 0 && minprice != inf){ // 未找到比当前价格便宜的油站,但是有相对便宜的
			totalprice += (nowprice * (cmax - leftdis/davg));
			leftdis = cmax*davg+nowdis - minpriceDis; // 计算便宜的=又还能走多远
			nowprice = minprice;
			nowdis = minpriceDis;
		}
		if(flag == 0 && minprice == inf){ // 未找到比当前便宜的油站并且也不能找到最便宜的 
			printf("The maximum travel distance = %.2f",maxdis);
			return 0;
		}
	}
	printf("%.2f\n",totalprice);
	return 0;
}

发表于 2020-03-09 12:55:43 回复(0)
另类一点的贪心算法,把每个加油站的油单独储存在车上。每到一个新的加油站,来计算路上消耗了  多少油,先消耗便宜的油,再消耗贵的油。计算完后,把贵的油按原价卖了,再在这个加油站把便宜的油买到满,如果新的加油站没有便宜油,就直接买满,最后在终点把剩下的油全部按原价卖了,相当于在每个加油站只买了自己需要的量。
#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;

typedef struct station{
    double Pi;
    double Di;
};

typedef struct carbox{    //车上每一款油的余量和价格
    double gas;
    double price;
};

bool comp(station x,station y){
    return x.Di<y.Di;
}

int main(){
    double Cmax,D,Davg;
    int N;
    station sta[500];
    carbox box[500];
    while(cin>>Cmax>>D>>Davg>>N){
        for(int i=0;i<N;i++){
            cin>>sta[i].Pi>>sta[i].Di;
        }
        sort(sta,sta+N,comp);
        double x=0;    //先检查是否能到终点
        for(int i=0;i<N;i++){
            if(x+Cmax*Davg>=sta[i].Di){
                x=sta[i].Di;
                if(i==N-1){
                    if(x+Cmax*Davg>=D){
                        x=D;
                    }
                    else{
                        x+=Cmax*Davg;
                    }
                }
            }
            else{
                x+=Cmax*Davg;
                break;
            }
        }
        if(x!=D){    //检查完成,开始进入计算
            printf("The maximum travel distance = %.2f",x);
            continue;
        }
        int hp=0,ep=0;  //油箱的油按价格递增排列,hp指向目前有余量最便宜的油
        box[0].gas=0;   //ep指向目前有余量最贵的油      
                box[0].price=sta[0].Pi;
        x=0;
        double c=0;
        double sum=0;
        for(int i=0;i<N;i++){
            double y=sta[i].Di-x;  //记录行驶路程
            x=sta[i].Di;
            while(y>0){            //耗油
                if(y>=box[hp].gas*Davg){
                    y-=box[hp].gas*Davg;
                    c-=box[hp].gas;
                    hp++;
                }
                else{
                    box[hp].gas-=y/Davg;
                    c-=y/Davg;
                    y=0;
                }
            }
            for(int j=hp;j<=ep;j++){
                if(box[j].price>=sta[i].Pi){
                    for(int k=j;k<=ep;k++){    //卖油
                        sum-=box[k].gas*box[k].price;
                        c-=box[k].gas;
                    }
                    ep=j;        //买便宜油
                    box[j].price=sta[i].Pi;
                    box[j].gas=Cmax-c;
                    sum+=box[j].gas*box[j].price;
                    c=Cmax;
                }
            }        //虽然这个加油站最贵,但还是买满
            if(c<Cmax){
                ep++;
                box[ep].price=sta[i].Pi;
                box[ep].gas=Cmax-c;
                sum+=box[ep].gas*box[ep].price;
                c=Cmax;
            }
        }
        double y=D-x;
        while(y>0){        //最后消耗
            if(y>=box[hp].gas*Davg){
                y-=box[hp].gas*Davg;
                c-=box[hp].gas;
                hp++;
            }
            else{
                box[hp].gas-=y/Davg;
                c-=y/Davg;
                y=0;
            }
        }
        for(int k=hp;k<=ep;k++){    //最后卖完
            sum-=box[k].gas*box[k].price;
        }
        printf("%.2f\n",sum);
    }
    return 0;
}

发表于 2021-02-20 14:02:12 回复(1)
贪心算法,先按油费对每个加油站进行排序,再从低油费的开始,让其走完最长的距离(即题中的cmax*davg),设置标志位 tag[] 记录是否走了。若当前油站可走的路有些已被其他油站走了,则跳过,对所有加油站都记录可走的路程。最后检验tag[] 是否有false,若有则说明不能走完整段路程,若全为true,则打印油费。
#include <iostream>
#include <algorithm>

using namespace std;

struct GasStation {
	double price;
	int distance;
};
bool ComparePrice(GasStation x, GasStation y) {
	return x.price < y.price;
}

int main() {
	int cmax, d, davg, n;
	while (cin >> cmax >> d >> davg >> n) {
		double currentprice = 0; // 当前油费
		bool tag[d + 1]; // 记录当前有哪段道路是从加油站出发能走的
		GasStation gasStation[n];
		
		for (int i = 1; i <= d; ++i) tag[i] = false;
		for (int i = 0; i < n; ++i) cin >> gasStation[i].price >> gasStation[i].distance;
		sort(gasStation, gasStation + n, ComparePrice); // 对油价按升序排 
		for (int i = 0; i < n; ++i) {  // 对tag[]进行记录, 并同时计算出 currentprice
			int currentdistance = 0; // 记录从这个加油站出发要用其油的距离 
			for (int j = gasStation[i].distance + 1; j <= gasStation[i].distance + cmax * davg; ++j) {
				if (tag[j] == false) { // 如果 tag[j]为假则可走 
					tag[j] = true;
					currentdistance++;
				}
				if (j == d || j == gasStation[i].distance + cmax * davg) { // 走到了尽头 
					currentprice += gasStation[i].price  * currentdistance / (davg * 1.0);
					break;
				}
			}
		}
		int fill = 1; // tag[]是否全为真的标志位
		double journey = 0;
		for (int i = 1; i <= d; ++i) {
			if (tag[i] == true) journey++;
			else {
				fill = 0;
				break;
			}
		}
		if (fill) printf("%.2f\n",currentprice);
		else printf("The maximum travel distance = %.2f\n", journey);
	}
	return 0;
}


发表于 2021-02-06 00:42:37 回复(0)
一个题折磨了我三个小时。。。
思路:
贪心:
1.起步时,如果没有距离是0的加油站,肯定走不了
2.在每个加油站都判断在最大油箱行驶距离内是否有比当前油价更便宜的
2.1如果没有,则加满(如果能直接到终点就加到终点的油就行了,这样比较便宜)。并且判断是否两个加油站的距离大于油箱的最大行驶距离,如果是直接输出;如果不是继续循环。
2.2如果有,则加到那个加油站的油就行了。
#include<iostream>
(720)#include<math.h>
#include<cstring>
(803)#include<vector>
#include<algorithm> 
using namespace std;
/*	
	贪心: 
	1.起步时,如果没有距离是0的加油站,肯定走不了 
	2.在每个加油站都判断在最大油箱行驶距离内是否有比当前油价更便宜的:
		如果没有,则加满。并且判断 是否两个加油站的距离大于油箱的最大行驶距离,如果是直接输出;如果不是继续循环。 
		如果有,则加到那个加油站的油就行了。 
*/ 
double Cmax,D,Davg,N,dis,price,gomax,ans,cur;//gomax为油加满的最大行驶距离。ans为最后的输出	。cur为当前的油量 
struct station{
	double p;
	double d;
	station(double pp,double dd):p(pp),d(dd){}
	inline bool operator <(const station &s)const{
		return d<s.d;
	}
};vector<station> vec;
int main(){
	while(cin>>Cmax>>D>>Davg>>N){
		vec.clear();
		bool ret=false;
		gomax=Cmax*Davg;ans=0,cur=0;
		for(int i=0;i<N;i++){
			cin>>price>>dis;
			vec.push_back(station(price,dis));
		}sort(vec.begin(),vec.end());//按距离进行排序 
		if(vec[0].d!=0){
			printf("The maximum travel distance = 0.00\n");
			ret=true;
		}else{
			for(int i=0;i<vec.size();i++){
				//cout<<"cur:"<<cur<<endl;
				//cout<<"i:"<<i<<endl;
				if(i==vec.size()-1){//判断当前油能不能到终点,如果能就break;如果不能则要加一点油。 
					double needdis=D-vec[i].d;
					if(needdis<=gomax){ //能到终点 
						if(cur<needdis/Davg){
							ans+=(needdis/Davg-cur)*vec[i].p;
							//cout<<"plus:"<<(needdis/Davg-cur)*vec[i].p<<endl;
						} 
					}else{
						printf("The maximum travel distance = %.2f\n",vec[i].d+gomax);
						ret=true;
					}break;
				} 
				double min=vec[i].p;int index=i;
				if(vec[i+1].d-vec[i].d>gomax){//后一个加油站的距离大于油箱的最大行驶距离。 
					printf("The maximum travel distance = %.2f\n",vec[i].d+gomax);
					ret=true;
					break;
				}
				for(int j=i+1;j<vec.size();j++){ 
					if(vec[j].d-vec[i].d<=gomax){
						if(vec[j].p<min){
							index=j;//找到了最近的一个比当前加油站便宜的加油站,加油加到此地就可以了。 
							break;
						}
					}else break;
				}
				if(index==i){//说明油箱最大行驶范围内就当前的加油站最便宜-->加满 
					if((D-vec[i].d)<=gomax){//如果终点就在眼前,加到终点就可以。 
						if(cur<(D-vec[i].d)/Davg){
							ans+=((D-vec[i].d)/Davg-cur)*vec[i].p;
						}break;
					}
					ans+=(Cmax-cur)*vec[i].p;
					cur=Cmax-(vec[i+1].d-vec[i].d)/Davg; 
				}else{
					double costoil=(vec[index].d-vec[i].d)/Davg; //行驶这段距离需要的油 
					if(costoil<=cur){//如果需要的油小于还剩的油,不用花钱。 
						cur-=costoil;
					}else{
						double needoil=costoil-cur;
						ans+=needoil*vec[i].p;
						//cout<<"plus:"<<needoil*vec[i].p<<endl;
						cur=0;
					} 
					i+=(index-i-1);//-1是因为i还要自加1一次; 
				} 
			}
		}
		if(!ret)printf("%.2f\n",ans);	
	}
	return 0;
}


发表于 2020-03-24 12:40:11 回复(2)
这道题写吐了我。。。
思路一开始想错了按价格升序, 后来才意识到应该按distance升序排列
过程思路在代码的注释中,只要把注释掉的代码取消注释,然后用例子输出一下就知道过程了!

核心部分的思路:

先按距离进行排序
设置sign=0
能否到达下一个加油站(默认我们先到第一个加油站,即初始化next=0)
      设置sign=1
      更新当前距离、剩余油量等参数,令当前站的下标为temp
      看看如果在此站 加满油 后能否到达比本站油价更低的
              如果有,看看当前剩余油够不够到,并令next=油价更低的站
                      如果能到的话,不用加了直接
                      如果不能到,加到足够撑到油价更低的站就行了
              如果没有更低的站
                      看看当前剩余的油能到终点,直接break
                      如果需要加一点才能到终点,加一点
                      如果不能到终点,加满,并令next=temp+1
如果sign=0,表示到不了下一个加油站了,按失败情况输出

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

struct station{
    double pri;
    double dis;
    bool visited;
    int index;
};

station sta[500];

bool cmp(station x, station y){
    if(x.dis==y.dis){
        return x.pri<y.pri;
    }
    else return x.dis<y.dis;
}

int main(){
    int N;
    double Cmax,wholeDis, aveDis;
    while(cin>>Cmax){
        cin>>wholeDis>>aveDis>>N;
        for(int i=0;i<N; i++){
            cin>>sta[i].pri>>sta[i].dis;
            sta[i].visited=false;
            sta[i].index=i;
        }

        sort(sta,sta+N,cmp);

        double currentDis=0;//当前行驶距离
        double currentFuel=0;//当前油量
        double wholePri=0;//总费用
        double consume=-1;//和目标加油站相差的油量
        double cost=-1;//(临时变量用于解释)
        int temp=-1;//临时变量,当前加油站的编号
        int fail=0;//无法到达目的地
        int succeed=0;//可以到达目的地
        double dif=-1;//(临时变量用于解释)
        double finalDis=0;//最远行驶距离
        double tempPri=-1;//(临时变量用于解释)
        int next = 0;
//        for(int i=0; i<N; i++)
//            cout<<sta[i].pri<<" "<<sta[i].dis<<endl;

        while(true){
            if(succeed)
                break;
            int sign=0;//默认到不了更远的加油站
            for(int i=next; i<N; i++){
                if(currentDis+currentFuel*aveDis>=sta[i].dis) {//能顺利到达下个加油站
                    sign = 1;
                    sta[i].visited = true;
                    currentFuel -= (sta[i].dis - currentDis) / aveDis;//到达此加油站剩余的油量
                    currentDis = sta[i].dis;//更新当前位置
                    temp = i;
//                    cout << "当前位于加油站" << temp << "  当前位置:" << currentDis << "  当前油量:" << currentFuel << endl;

                    //计算下一个要去的加油站
                    double minPri = 99999999999;

                    int flag = 0;//默认能到达的加油站没有比它价格更小的
                    for (int j = temp + 1; j < N; j++) {
                        if (currentDis + Cmax * aveDis >= sta[j].dis && !sta[j].visited&&sta[j].pri < sta[i].pri) {//此加油站能到达的,比它价格更小的
                            next = j;
                            flag = 1;
                            break;
                        }
                    }
                    if (flag) {//加到够到目标站的就行了
                        if(currentDis + currentFuel*aveDis <sta[next].dis) {//如果当前的油不够到目标站的
                            consume = (sta[next].dis - currentDis) / aveDis;//需要的油量
                            dif = (consume - currentFuel);
                            cost = (consume - currentFuel) * sta[i].pri;
                            wholePri += (consume - currentFuel) * sta[i].pri;
                            currentFuel = consume;
                            flag = 0;
//                            cout << "下一站想去:" << next << "  目标位置:" << sta[next].dis << "  需要油量:" << consume << endl;
//                            cout << "加了" << dif << " 花费:" << cost << endl;
                            break;
                        }
                        else{
//                            cout<<"此加油站不加油"<<endl;
                            break;
                        }

                    } else {//没找到价格更低的,加到下个重点或者全都加满
                        if (currentDis + currentFuel * aveDis >= wholeDis) {//剩余油足够到达终点
                            succeed = 1;//不加了
                            break;
                        } else if (currentDis + Cmax * aveDis >= wholeDis) {//再加一些能到达终点
                            consume = (wholeDis - currentDis) / aveDis;
                            dif = (consume - currentFuel);
                            cost = (consume - currentFuel) * sta[i].pri;
                            wholePri += (consume - currentFuel) * sta[i].pri;
                            succeed = 1;
//                            cout << "加了" << dif << " 花费:" << cost << endl;
                            break;
                        } else {//加满
                            dif = (Cmax - currentFuel);
                            cost = (Cmax - currentFuel) * sta[i].pri;
                            wholePri += (Cmax - currentFuel) * sta[i].pri;
                            currentFuel = Cmax;
//                            cout << "加了" << dif << " 花费:" << cost << endl;
//                            cout << "####已加满油####" << endl;
                            next=temp+1;
                            break;
                        }
                    }
                }
            }

            if(sign==0){
                finalDis=currentDis+Cmax*aveDis;
                fail=1;
                break;
            }
        }
        if(succeed)
            printf("%.2lf\n",wholePri);

        if(fail)
            printf("The maximum travel distance = %.2lf\n",finalDis);
    }
    return 0;
}


编辑于 2020-02-05 21:16:24 回复(0)
package com.speical.first;

import java.util.Scanner;
/** 
* 加油站问题
* 
* 这不能用背包来做,因为情况非常复杂
* 因为到每个加油站的状态并不是固定,因为不是每次都到加油不是把油正好加到下一个加油站
* 所以我们采用贪心的做法
* 情况如下:
* 1.刚开始,我们的车没油,所以要找0距离油价最少的那一个
* 2.然后考察车的最大距离里的所有加油站,看看是否有比当前油价少的加油站
*     1.若有,我们只需加油加到正好到该加油站即可,因为之后的距离我想用便宜的油价
*   2.若没有,我们只能在找到最大距离内大于当前油价最小的那一个即可,但是此时我们要
*   把油加满,因为当前油价便宜啊,加满之后,到大于当前油价最小的那一个的加油站时,继续考察
*   到的那个加油站即可。之后我们要再次根据距离,补充油(注意此时油箱是有油的,上次油价便宜)。
*   
* 以上在油价时,我们用leftdis表示油箱中剩余的油能走的距离,所以在根据距离算钱,要减去该距离
* @author special
* @date 2018年1月29日 上午11:40:18
*/
public class Pro152 {

    static double Cmax, D, Davg, onceMaxDis;
    static int N;
    static final double MIN = Double.MAX_VALUE;

    static class Node{
        double price;
        double dis;

        public Node(double price, double dis){
            this.price = price;
            this.dis = dis;
        }
    }
    private static boolean less(Node node1, Node node2){
        if(node1.dis < node2.dis || node1.dis == node2.dis
                                 && node1.price < node2.price){
            return true;
        }
        return false;
    }

    public static void insertSort(Node[] nodes, int n){
        for(int i = 1; i < n; i++){
            for(int j = i; j > 0 && less(nodes[j], nodes[j - 1]); j--){
                Node temp = nodes[j];
                nodes[j] = nodes[j - 1];
                nodes[j - 1] = temp;
            }
        }
    }

    public static void solution(Node[] nodes){
        boolean flag = true;
        int index = 0;
        double nowPerPrice, nowDis = 0, maxDis, totalPrice = 0, 
               leftDis = 0, minPrice = MIN, minDis = 0;
        while(nodes[index].dis == 0){  //找0距离的最小的油价的加油站
            minPrice = Math.min(minPrice, nodes[index].price);
            index++;
        }
        if(index == 0){ //若没有,说明没法走
            flag = false;
        }else{
            nowPerPrice = minPrice;  //更新当前的油价
            boolean isShorter = false; //是否存在比当前油价小的加油站
            while(nowDis < D){
                maxDis = nowDis + onceMaxDis; //当前能到的最大位置
                minPrice = MIN; 
                minDis = 0;
                isShorter = false;
                for(int i = index; i <= N && nodes[i].dis <= maxDis; i++){
                    if(nodes[i].dis == nowDis) continue;
                    if(nodes[i].price < nowPerPrice){  //因为我们排序的缘故,保证了大于当前位置的第一个加油站必是同位置的最小的
                        totalPrice += nowPerPrice * (nodes[i].dis - nowDis - leftDis) / Davg; //注意要减去油箱中剩余的油
                        nowPerPrice = nodes[i].price;
                        nowDis = nodes[i].dis;
                        leftDis = 0;
                        isShorter = true;
                        index = i;
                        break; //直接跳出即可,同位置之后的油价肯定是大于等于当前的
                    }
                    if(nodes[i].price < minPrice){ //我们还要记录最大距离的最小的油价,万一我们找不到更实惠,就可以将就一下
                        minPrice = nodes[i].price;
                        minDis = nodes[i].dis;
                        index = i;
                    }
                }
                if(!isShorter){ // 真没有比当前更实惠的
                    if(minPrice == MIN){ //油价还是天价,说明我们的车不行,跑不到加油站
                        nowDis += onceMaxDis; //在当前的加油站,加满,跑完得了,因为车不行
                        flag = false;
                        break;
                    }else{
                        totalPrice += nowPerPrice * (onceMaxDis - leftDis) / Davg; //否则就是我们加满油,跑到还可以接受的加油站
                        leftDis = onceMaxDis - (minDis - nowDis); //跑到那里之后,我们会剩余多少油
                        nowDis = minDis;
                        nowPerPrice = minPrice;
                    }
                }
            }
        }
        if(flag){
            System.out.format("%.2f\n", totalPrice);
        }else{
            System.out.format("The maximum travel distance = %.2f\n", nowDis);
        }
    }
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        while(input.hasNext()){
            Cmax = input.nextDouble();
            D = input.nextDouble();
            Davg = input.nextDouble();
            N = input.nextInt();

            Node[] nodes = new Node[N + 1];
            onceMaxDis = Cmax * Davg;  //油箱满的时候,走的最大距离

            for(int i = 0; i < N; i++){
                nodes[i] = new Node(input.nextDouble(), input.nextDouble());
            }
            nodes[N] = new Node(0, D);
            insertSort(nodes, N + 1); // 插入排序,为了减少内循环的次数
            solution(nodes);
        }
    }
}
发表于 2018-01-30 10:16:50 回复(0)
核心思路是,开一个数组,长度为D,每个单位距离视为一个数组元素,初始为-1。
把加油站按价格从高到低排序,从最贵的开始,每个站都把油箱加满,这段距离上的数组元素等于每距离花费。
然后到价格低的站,再加满,即用便宜油代替贵油。直到最便宜的站之后,每个单位距离用的就是最便宜的油。
遍历整个数组,如果能遍历完,则累加起来就是最便宜花费。
如果遇到-1,表示就算前面用了最贵的油,也跑不到这个距离,也就是最大距离。

说个搞笑的也是让我想发这个讨论的原因,我一开始数组元素初始为0,结果最后一个样例怎么都通过不了。把样例输出一看,好家伙,40距离处的加油站价格为0,遍历到这里break了,所以输出最大距离40。我只想问一下,这种0元购的良心加油站在哪?
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct station{
	double pi;
	int di;
};
bool cmp(station a,station b){
	return a.pi>b.pi;
}
int main(){
	int cmax,d,davg,n,i,j;
	cin>>cmax>>d>>davg>>n;
	double money[d],sum=0,dist;
	station sta[n];
	for(i=0;i<n;i++)cin>>sta[i].pi>>sta[i].di;	
	sort(sta,sta+n,cmp);
	for(j=0;j<d;j++)money[j]=-1;
	for(i=0;i<n;i++){
		for(j=sta[i].di;j<sta[i].di+cmax*davg&&j<d;j++){
			money[j]=sta[i].pi/davg;
		}
	}
	for(j=0;j<d;j++){
		if(money[j]==-1)break;
		sum+=money[j];
	}
	if(j==d)printf("%.2f\n",sum);
	else {
		dist=(double)j;
		printf("The maximum travel distance = %.2f\n",dist);
	}

	return 0;
}

编辑于 2024-02-03 20:54:25 回复(1)
#include <iostream>
#include <algorithm>
#include <iomanip>

using namespace std;

struct gasT{
    double price;
    int  pos;
};

bool compareByPos(gasT a,gasT b){
    if(a.pos==b.pos){
        return a.price<b.price;//位置相同以油价升序排列
    }else{
        return  a.pos<b.pos;//以位置升序排列
    }

}
int main() {
    int capacity, TotalDistance, DistancePerGas, GasTnum;
    while (cin >> capacity >> TotalDistance >> DistancePerGas >> GasTnum) {
            gasT pos[GasTnum+1];
            for (int i = 0; i < GasTnum; ++i) {
                cin >> pos[i].price >> pos[i].pos;
            }
            sort(pos, pos + GasTnum, compareByPos);

            pos[GasTnum].price=0;//把终点设为加油站
            pos[GasTnum].pos=TotalDistance;

            //贪心思路:每到一个加油站向后查询一个满油能跑距离内是否有比目前油价便宜的最近加油站(加油至足够到达该目的加油站
            //结束条件:终点距离加油站满油距离内,距离内部的加油站价格都比目前高,加油至终点,结束查询,输出消费
            int fullGasRun = capacity * DistancePerGas;//满油能跑的距离
            double leftRun = 0;//还能跑多远
            double spend = 0;//目前消费
            double run = 0;//目前走了多远
            int t = 0;//目前到的加油站-1(以距离起点位置升序排号)

            while(t<GasTnum+1){
                if(pos[0].pos!=0){//起点没有加油站,退出输出
                    cout << "The maximum travel distance = " << 0.00 << endl;
                    break;
                }
                int cheap=0;
                for(int j=t+1;pos[j].pos-pos[t].pos<=fullGasRun;j++){//1.位置要在满油距离内//在目前的加油站向后遍
                        if(pos[t].price >= pos[j].price){//2.找比目前便宜的第一个加油站(一样价格也可以)
                            if(leftRun<pos[j].pos - pos[t].pos){//余油不足,把油加到刚好够到达便宜加油站的量即可
                                spend = (pos[j].pos - pos[t].pos - leftRun)* pos[t].price /DistancePerGas+spend;
                                leftRun =  pos[j].pos - pos[t].pos;
                            }
                            run += pos[j].pos - pos[t].pos;//余油充足与否都需要的操作
                            leftRun = leftRun -pos[j].pos + pos[t].pos;
                            t=j;
                            cheap=1;
                            break;//退出for的小循环
                        }
                }
                if(leftRun+pos[t].pos>=TotalDistance){//可到达终点,输出
                    cout << fixed << setprecision(2) << spend << endl;
                    break;
                }
                if(cheap==0){//没有便宜的加满
                    spend += (fullGasRun - leftRun) / DistancePerGas * pos[t].price;
                    leftRun = fullGasRun;
                }else{
                    continue;//找到便宜的加油站就不执行下面的操作
                }
                if(pos[t+1].pos-pos[t].pos>fullGasRun){//下一个油站不在满油距离内(把leftrun清空)
                    run+=leftRun;
                    cout<< fixed << setprecision(2) << "The maximum travel distance = "<< run << endl;
                    break;
                }
                t++;//走到下一个加油站
                leftRun-= pos[t].pos-pos[t-1].pos;
                run+=pos[t].pos-pos[t-1].pos;
            }
        }
    }



发表于 2023-01-20 18:42:27 回复(0)
我不理解 一开始油箱是空的 那岂不是一开始就没法移动吗?
发表于 2024-08-31 12:11:31 回复(0)
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;

// 油箱最大容量 / 杭州与目的地距离 / 每单位油可行驶距离 / 加油站总数
// 目的:找到到达目的地最便宜的方案 / 无法到达则输出最远距离
// 无法到达时
    // 在当前位于的站点驶出最远距离也找不到下一站或终点
// 可到达时
    // 让加油站按距离排序
    // 查询最远可达距离内比当前价格便宜且最近的,加油到正好可到达对应位置,重复此过程
    // 若找不到更便宜的则先看是否能到达目的地,能则加到正好到达,否则加满,到下个价格最接近的位置,重复此过程
   
struct Gas_station{
    double price;
    int pos;
};

bool cmp(Gas_station a,Gas_station b){ // 边界处理:对有多个数据的项进行排序,如果排序数据相同还要考虑其他数据
    if(a.pos!=b.pos) return a.pos<b.pos;
    else return a.price<b.price;
}

// 找到能到达范围内,价格比当前站低,或最接近当前站的
int findnextprice(vector<Gas_station> station,int stationid,int dmax,int dis){
    int minpriceid=stationid+1; // 先记录下一站的下标

    // 当前站驶出最远距离到不了终点,也到不了下一站
    if(station[stationid].pos+dmax<dis && station[minpriceid].pos>station[stationid].pos+dmax){
        return -1;
    }

    // 起码有驿站在可到达范围内,则可继续向前走
    for(int i=stationid+1;station[i].pos<station[stationid].pos+dmax && i<station.size();i++){ // 从当前下一项开始找最低
        if(station[i].pos==station[stationid].pos) continue;
        if(station[i].price<=station[minpriceid].price){
            minpriceid=i;
            if(station[minpriceid].price<station[stationid].price) break; // 找到任意低于当前价格就立即返回,否则就是区间中最低价
        }
    }
    return minpriceid;
}

int main() {
    double tankmax,davg;
    int n,dis;
    while (cin >> tankmax >> dis >> davg >> n) {
        Gas_station tmp;
        vector<Gas_station> station;
        for(int i=0;i<n;i++){
            scanf("%lf%d",&tmp.price,&tmp.pos);
            station.push_back(tmp);
        }
        sort(station.begin(),station.end(),cmp); //按距离排序,相同距离按价格低到高排序

        int stationid =0;
        double dmax = tankmax*davg;
        double tank = 0;
        double pricetotal=0;
        int next;
        while(true){
            // 边界处理:找下一项只发生在不是最后一项的情况下
            if(stationid!=station.size()-1) next=findnextprice(station, stationid, dmax,dis);

            // 若无法到达,则最远距离为在当前站点驶出最远距离
            if(next==-1){
                printf("The maximum travel distance = %.2lf",station[stationid].pos+dmax);
                break;
            }
           
            // 可到达下一项的情况
            if(station[next].price<station[stationid].price){ // 若下一项价格更低,从当前站直接过去
                pricetotal+=(((station[next].pos-station[stationid].pos)-(tank*davg))/davg)*station[stationid].price; // 注意油箱内剩余的油
                tank=0; // 直接清零的原因:
                // 需要加满油的情况只发生在"当前项是可到达范围内价格最低的",只能去价格第二低的站时
                // 而此时又有下一个价格更低的站
                // 若油箱足够从需要加满油的最低价站开到此时找到的价格比当前更低的站,那么当时就不会开到此站
                // 所以一定是只够开到此站而到不了下一个更低价站,则在此站补足到达需要的油,然后全部用完以到达
                stationid=next;
            }
            else{ // 找不到价格更低的站
                if(station[stationid].pos+dmax>dis ){ // 若能直接到终点
                    pricetotal+=(((dis-station[stationid].pos)-(tank*davg))/davg)*station[stationid].price;
                    break;
                }
                else{   // 若不能到终点,就只能去价格第二低的站
                    pricetotal+=(tankmax-tank)*station[stationid].price;
                    tank=tankmax-(station[next].pos-station[stationid].pos)/davg; // 此时把油加满
                    stationid=next;
                }
            }
        }
        if(next!=-1) printf("%.2lf\n",pricetotal);
    }
}
发表于 2024-02-24 15:45:32 回复(0)
贪心+区间重叠的问题。
对所有的加油站都是一个区间,假设在该加油站能走多远[a,b]。对于所有的区间,按照价格升序排列。
处理所有区间,如果当前区间路段没有走过,就全部用这个加油站的钱,如果跟某个区间重叠,那么重叠部分就不用走(油价便宜的先处理了)。并把处理后的实际路程计算金额加入统计,然后加入处理过数组。
全部处理后,把处理的区间重新按照左端点排序,然后看看最大连续路径是不是到达终点。
编辑于 2024-02-16 16:10:57 回复(0)
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <vector>

using namespace std;
using pdi = pair<double, int>;

const int N = 510;

double c, d, s; // capacity, distance, dis per gas, stations
int n;

int main() {
    while (cin >> c >> d >> s >> n) {
        vector<pdi> ps(n);
        for (int i = 0; i < n; i++) {
            cin >> ps[i].first >> ps[i].second;
        }
        sort(ps.begin(), ps.end(), [](pdi p1, pdi p2) {
            return p1.second < p2.second;
        });
        double price = 0.0;
        double dis = 0;
        auto current = ps.begin();
        double capa = 0;
        while (current != ps.end()) {
            auto ith = find_if(current + 1, ps.end(), [&](pdi p) { // false 表示找不到性价比高的或者开不到性价比高的那一站
                double max_dis = current->second + c * s;
                if (p.second > max_dis) {
                    return false;
                }
                if (p.first <= current->first) {
                    return true;
                } else {
                    return false;
                }
            });
            if (ith == ps.end()) { // 找不到h
                double max_dis = current->second + c * s;
                auto next = current + 1;
                if (max_dis < next->second) { // 开不到下一站
                    cout << "The maximum travel distance = " << fixed << setprecision(2) << max_dis << endl;
                    break;
                } else if (max_dis >= d) { // 能直接开往终点
                    price += (d - current->second - capa * s) / s * current->first;
                    cout << fixed << setprecision(2) << price << endl;
                    break;
                } else { 
                    // 不能开往终点, 加满油, 开到下一站
                    price += (c - capa) * current->first;
                    capa = c - (next->second - current->second) / s;
                    current = next;
                }
            } else { // 找到h, 加油至h
                double cd = current->second + capa * s;
                if (cd >= ith->second) { // 油够用
                    capa -= (ith->second - current->second) / s;
                    current = ith;
                } else { // 油不够, 加油加到刚好能到ith
                    price += ((ith->second - capa * s - current->second) / s) * current->first;
                    capa = 0.0;
                    current = ith;
                }
            }
            if (current == ps.end() - 1) {
                double max_dis = current->second + c * s;
                if (max_dis >= d) {
                    price += (d - current->second - capa * s) / s * current->first;
                    cout << fixed << setprecision(2) << price << endl;
                    break;
                } else {
                    cout << "The maximum travel distance = " << fixed << setprecision(2) << max_dis << endl;
                    break;
                }
            }
        }
    }
}

编辑于 2024-01-25 09:32:29 回复(0)