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: 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.

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 = X" where 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

这是一道考察贪心算法的题目,为了达到花最少的钱到达终点或是跑最远距离的目的,我们很难整体的去把握这个问题,通过贪心算法,可以把整体问题化为局部问题,只站在当前的角度分析最贪婪(最优)的抉择,从而得到问题的最优解,贪心问题的困难之处在于对问题的分析和情况分类,一旦情况考虑的不够周全,就可能满盘皆输。

对于这道题目,我们分如下的情况进行讨论。

对于当前站点S,所能到达的最大范围即满油量所能到达的距离,设满油量能前进的距离为maxToGo,则在S到S+maxToGo范围内,分如下情况进行考虑:

Ⅰ此范围内有加油站

     ①有比当前站点便宜的加油站,因为只从最小的局部考虑问题,如果有多个比当前便宜的,到达那个最近的而不是最便宜的(只需要在找到第一个比S便宜的站点时break即可)。

     ②全部比S更贵(易错点)

         2.1 如果从S无法到达终点,则选择最便宜的那个,从S加满油到达那个站点。

         2.2 如果从S可以直接到达终点,则从S加油至能到达终点,直接开到终点。

Ⅱ此范围内无加油站

     ①如果从S可以直接到达终点,则加到能到达终点,直接到达。

     ②如果从S无法到达终点,加满油,能跑多远跑多远。


具体实现为用结构体存储站点信息,压入vector按照升序排序,从前到后处理各个站点,用cur表示当前所在的站点,当cur为最后一个站点之后的范围时,结束循环,具体代码如下:

#include <iostream>
#include <vector>
#include <stdio.h>
#include <iomanip>
#include <algorithm>

using namespace std;

#define INF 99999999

struct GasStation{

    double price;
    double dis;

    GasStation(double _p, double _d) : price(_p), dis(_d) {}

};

int compare(GasStation a, GasStation b){

    return a.dis < b.dis;

}

int main()
{
    int cons,gasCnt;
    double cap;
    double dis;
    double price;
    double dist;
    vector<GasStation> stations;
    cin >> cap >> dis >> cons >> gasCnt;
    double maxToGo = cap * cons;
    for(int i = 0; i < gasCnt; i++){
        scanf("%lf %lf",&price,&dist);
        stations.push_back(GasStation(price,dist));
    }
    sort(stations.begin(),stations.end(),compare);

    if(stations[0].dis > 0){
        printf("The maximum travel distance = 0.00\n");
        return 0;
    }

    int cur = 0;
    double now_cap = 0;
    double sumPrice = 0;
    int curEnd = stations.size();
    double stationPrice = 0;
    double stationDis = 0;
    int hasStation = 0;
    int dest = 0;

    // 如果有多个起点加油站,选择那个最便宜的加油。
    // 事实证明题目中并没有此类不符合实际的陷阱。
    int minPrice = stations[0].price;
    for(int i = 0; i < stations.size(); i++){
        if(stations[i].dis == 0){
            if(minPrice > stations[i].price){
                cur = i;
            }
        }else break;
    }

    while(cur < curEnd){
        stationPrice = stations[cur].price;
        stationDis = stations[cur].dis;
        dest = -1;
        hasStation = 0;

        for(int i = cur + 1; i < stations.size(); i++){ // 首先判断当前站点之后有没有可以到达的
            if((stations[i].dis - stationDis) <= maxToGo){ // 发现有可到达的站点,再找出最近且最便宜的。
                hasStation = 1;
                // 找出最便宜的有两种情况,第一是有比当前站点便宜的,到达最近的满足条件的这样的站点。
                // 或者都比当前站点贵,则加油到能到达最便宜的那个。
                // 这两个判断是冲突的,因为有比当期便宜的时候选择的不是那个最便宜的而是最近的,都贵的时候找的是最便宜的
                // 因此先判断有没有比当前便宜的,没有再进一步找那个贵中最便宜的。
                if(stationPrice > stations[i].price){ // 找到了更便宜的,在这里中断查找,保证找到的是最近的。
                    dest = i;
                    break;
                }
            }else{ // 都没有可到达的站点了。
                break;
            }
        }
        if(hasStation != 1){ // 没有可到达站点
            if((dis - stationDis) <= maxToGo){ // 能跑到终点,则加油到可以跑到终点
                double need = dis - stationDis;
                if(now_cap * cons >= need){ // 油足够到达
                    break;
                }else{ // 油不够,加到能跑到终点
                    double last = (need - now_cap * cons);
                    sumPrice += (last / cons) * stationPrice;
                    break;
                }
            }else{ // 跑不到终点,能跑多远跑多远
                double sumDis = stationDis + cap * cons;
                printf("The maximum travel distance = %.2lf\n",sumDis);
                return 0;
            }
        }else{ // 有可以到达的站点
            if(dest != -1){ // 找到了比当前便宜且距离当前最近的加油站,加油到跑到那里,然后继续在那个站点考虑
                double need = stations[dest].dis - stationDis;
                if(need <= now_cap * cons){ // 油足够到达
                    now_cap -= need / cons;
                }else{ // 油不够,补齐
                    sumPrice += (need - now_cap * cons) / cons * stationPrice;
                    now_cap = 0; // 跑过去就没有油了
                }
                cur = dest;
            }else{ // 没有便宜的,选择那个最便宜的加油跑过去。

                // !!!先看能否到终点,能到就直接到终点,一定注意这种情况!!!
                if((dis - stationDis) <= maxToGo){
                    double need = dis - stationDis;
                    if(now_cap * cons < need){
                        sumPrice += (need - now_cap * cons) / cons * stationPrice;
                    }
                    break;
                }


                int minPrice = INF;
                int minCur = -1;
                for(int i = cur + 1; i < stations.size(); i++){
                    if((stations[i].dis - stationDis) < maxToGo){
                        if(stations[i].price < minPrice){
                            minPrice = stations[i].price;
                            minCur = i;
                        }
                    }else{
                        break;
                    }
                }
                cur = minCur;
                sumPrice += (cap - now_cap) * stationPrice;
                now_cap = cap - (stations[cur].dis - stationDis) / cons;
            }
        }


    }
    printf("%.2lf\n",sumPrice);

    return 0;
}


转载于:https://www.cnblogs.com/aiwz/p/6154162.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
图着色问题是指给定一个无向图,尝试用最少的颜色对每个顶点进行染色,使得任意两个相邻的顶点颜色不同。这是一个经典的NP难问题,没有有效的多项式时间算法,但是可以使用贪心算法来近似解决。 贪心算法的基本思路是:首先按照某种规则选择一个顶点进行染色,然后依次选择其它未染色的顶点,并尝试用最少的颜色进行染色。如果当前顶点的相邻顶点都已经染过色,那么就选择一个未使用的颜色进行染色,否则选择一个不与相邻顶点颜色相同的颜色进行染色。 C++代码实现如下: ```c++ #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAXN = 100; // 最大顶点数 int n, m; // n 表示顶点数,m 表示边数 vector<int> G[MAXN]; // 存储图的邻接表 int color[MAXN]; // 存储每个顶点的颜色 int greedy_coloring() { int ans = 0; for (int u = 0; u < n; ++u) { bool used[MAXN] = { false }; for (int i = 0; i < G[u].size(); ++i) { int v = G[u][i]; if (color[v] != -1) { used[color[v]] = true; } } for (int i = 0; ; ++i) { if (!used[i]) { color[u] = i; ans = max(ans, i); break; } } } return ans + 1; // 返回使用的颜色数 } int main() { cin >> n >> m; for (int i = 0; i < m; ++i) { int u, v; cin >> u >> v; G[u].push_back(v); G[v].push_back(u); } fill(color, color + n, -1); cout << "使用的颜色数:" << greedy_coloring() << endl; for (int i = 0; i < n; ++i) { cout << "顶点 " << i << " 的颜色是 " << color[i] << endl; } return 0; } ``` 该代码中,我们使用邻接表存储图,并使用一个数组 `color` 存储每个顶点的颜色,初始化为 -1 表示未染色。在 `greedy_coloring` 函数中,我们依次遍历每个顶点,对于每个未染色的顶点,我们枚举可用的颜色,选取第一个未被使用的颜色进行染色。在枚举颜色的过程中,我们需要检查相邻顶点的颜色,标记已经使用的颜色。 该算法的时间复杂度为 $O(n^2)$,因为对于每个顶点,都需要遍历其相邻顶点。当然,使用邻接表存储图可以优化到 $O(m)$ 的时间复杂度。该算法是一种近似算法,不一定能够得到最优解,但是在实际应用中效果很好。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值