PAT(Advanced) 1033. To Fill or Not to Fill 典型贪心算法

1033. To Fill or Not to Fill (25)

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.

Input Specification:

Each input file contains one test case. For each case, the first line contains 4 positive numbers: C​max​​ (≤ 100), the maximum capacity of the tank; D (≤30000), the distance between Hangzhou and the destination city; D​avg​​ (≤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: P​i​​ , the unit gas price, and D​i​​ (≤D), the distance between this station and Hangzhou, for i=1,⋯,N. All the numbers in a line are separated by a space.

Output Specification:

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 = Xwhere X is the maximum possible distance the car can run, accurate up to 2 decimal places.

Sample Input 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

Sample Output 1:

749.17

Sample Input 2:

50 1300 12 2
7.10 0
7.00 600

Sample Output 2:

The maximum travel distance = 1200.00

  这次题目理解算是正确了,也明白要用贪心算法,但是第一遍写的时候只通过了一个例子。当时我的想法是将加油站按照距离升序排列,每一次在加满油能到达的范围内搜索一个性价比最高的加油站和我当前所在的加油站比较:如果当前加油站性价比更高,那么我就加满油过去再进行判断;如果当前加油站性价比更低,那我就应该加尽量少的油到新的加油站去消费。然而PAT一片绿灯告诉我:大错特错!

  方向应该是没错的,那应该是细节出了问题,而且和测试用例的对比以后我发现我只多了1块钱!一番折腾后发现这个贪心“不够贪”:每次搜索时应该在找到第一个更高性价比的加油站的时候就停下来 。因为这样的加油站存在即证明一件事:这个加油站之后的一段路的油应该让它来承包,毕竟它性价比更高啊!我现在所在的小破站的油多一滴我都不用!这才叫贪心啊!一块钱也是钱啊!那如果找不到更好的加油站呢?很简单,我现在这个最便宜,那我肯定加满油往前冲,在第二便宜的加油站停下来再搜索。

于是整体思路如下:

1. 升序排列
2. 洗脑:告诉自己现在所处的加油站很贵,不能加太多了。在可达范围内找一个最近且油更便宜的加油站,毫不犹豫计算出我最少需要加多少油,坚决不让黑心加油站多挣一分钱,上路。
3. 发现不存在这样的加油站,于是恍然大悟:原来这个加油站是良心加油站,我得拉满才对得起他。于是把油加满,然后在可达范围内找一个第二便宜的站锁定为目标,上路。
4. 当到达最后一站时,此时已经没有余地给我贪了,石油公司已经被我薅干净了,我才想起来原来此行另有目的,这时回归距离问题:我需要多少油才能到终点or我加满油能跑多远。

代码如下:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct station{
    int d;
    double w;
    friend bool operator<(const station &a, const station &b){
        return a.d<b.d;
    }
};
vector<station> stas;
double cmax,davg;
int n,dis;
int main(){
    scanf("%lf%d%lf%d",&cmax,&dis,&davg,&n);
    while(n--){
        station t;
        scanf("%lf%d",&t.w,&t.d);
        stas.push_back(t);
    }
    sort(stas.begin(),stas.end());
    if(stas[0].d != 0) {
        printf("The maximum travel distance = 0.00");
        return 0;
    }
    int dsum=0,curst=0,dmax=cmax*davg;//total distance,current station,how far we can reach with filled tank
    double cost=0.0,curgas=0.0;//total cost, gas remained
    while(dsum<dis){
        double MIN=0x3fffffff;
        int u=-1;
        double dgas;
        for(int i=curst+1;i<stas.size();i++){//find a station cheaper than this one (within reach)
            if(stas[i].w<stas[curst].w && stas[i].d-stas[curst].d<=dmax){
                MIN=stas[i].w;
                u=i;
                break;
            }
        }        
        if(u==-1) {//if current station is the cheapest one within reach       
            if(dsum+dmax>=dis){//if the destination is reachable
                int lastd=dis-dsum;
                dgas=(double)lastd/davg-curgas;
                cost+=dgas*stas[curst].w;
                dsum=min(stas[curst].d+dmax,dis);
                break;
            }
            for(int i=curst+1;i<stas.size();i++){//find the cheapest one except the one we are now at(within reach,of cause)
                if(stas[i].w<MIN && stas[i].d-stas[curst].d<=dmax){
                    MIN=stas[i].w;
                    u=i;
                }
            }
        }
        if(u==-1){//still can't find MIN, hence we are now at the last station 
            int lastd=min(dis-dsum,dmax);
            dgas=(double)lastd/davg-curgas;
            cost+=dgas*stas[curst].w;
            dsum=min(stas[curst].d+dmax,dis);
            break;
        }
        if(MIN<=stas[curst].w){//there is a cheaper station within reach 
            dgas=(double)(stas[u].d-stas[curst].d)/davg-curgas;
            curgas=0;
        }
        else{//just fill up the gas tank
            dgas=cmax-curgas;
            curgas=cmax-(double)(stas[u].d-stas[curst].d)/davg;
        }
        cost+=dgas*stas[curst].w;
        dsum=stas[u].d;
        curst=u;
    }
    if(dsum==dis) printf("%.2f",cost);
    else printf("The maximum travel distance = %.2f",(double)dsum);
    return 0;
}

总结:

  • 多练,想到贪心是大方向上的直觉,而细节上的直觉需要更多的练习来培养。
  • 对比了一下这题解法和Dijkstra算法,感觉自己是在分解问题的时候没能找到最小(准确的说是最恰当)的子问题,导致我虽然每个子问题都优化了,但并不是最佳优化。这有点类似于找极值和找最值,我感觉在之后的学习中一定会遇到类似的问题,需要我加深这一块的理解。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值