贪心策略

本文来源于计算机考研――机试指南(第2版)

贪心策略常用于求解最优化问题,其核心思想是,总是选择当前状态下最优的策略。也就是说,它并不以整体的最优进行考虑,而只考虑当前这一步。因此,这种方法能够得到局部最优解,最后往往也能够获得全局较优解,但并不保证收敛到全局最优解。

既然贪心策略并不保证收敛到全局最优解,那么为什么在最优化问题还要使用它呢?原因是对于特定的最优化问题,贪心策略保证一定能够保证收敛到全局最优解。这类最优化问题具备无后效性,即某个状态以前的过程不会影响以后的状态,而只与当前状态有关,只要最优化问题具有这样的性质,便能保证使用贪心策略一定能够获得最优解。

1.简单贪心

KY8:鸡兔同笼

题目描述

一个笼子里面关了鸡和兔子(鸡有2只脚,兔子有4只脚,没有例外)。已经知道了笼子里面脚的总数a,问笼子里面至少有多少只动物,至多有多少只动物。

输入描述:

每组测试数据占1行,每行一个正整数a (a < 32768)

输出描述:

输出包含n行,每行对应一个输入,包含两个正整数,第一个是最少的动物数,第二个是最多的动物数,两个正整数用一个空格分开
如果没有满足要求的答案,则输出两个0。

输入

2
3
20

输出

0 0
5 10

分析

  • 在计算最多动物数的时候,优先考虑脚少的鸡;计算最少动物数的时候,优先考虑脚多的兔子,直到脚的数目无法构成兔子时,再考虑脚少的鸡。
  • 无论时兔子还是鸡脚的个数都是偶数,如果总脚数是奇数,问题无解。
  • 总脚数是偶数,求最多动物数,全部考虑成鸡。
  • 总脚数是偶数,求最少动物数,优先考虑兔子,最后结果只会是全是兔子或者一群兔子和一只鸡。

代码

#include <iostream>
#include <cstdio>

using namespace  std;

int main(){
    int a;
    while(cin>>a){
        int minimum = 0;
        int maxmum = 0;
        if(a % 2 == 0){
            minimum = a/4 +(a%4)/2;
            maxmum = a/2;
        }
        cout<<minimum<<" "<<maxmum<<endl;
    }
    return 0;
}

HDU:FatMouse' Trade

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 51783 Accepted Submission(s): 17369

Problem Description

FatMouse prepared M pounds of cat food, ready to trade with the cats guarding the warehouse containing his favorite food, JavaBean.
The warehouse has N rooms. The i-th room contains J[i] pounds of JavaBeans and requires F[i] pounds of cat food. FatMouse does not have to trade for all the JavaBeans in the room, instead, he may get J[i]* a% pounds of JavaBeans if he pays F[i]* a% pounds of cat food. Here a is a real number. Now he is assigning this homework to you: tell him the maximum amount of JavaBeans he can obtain.

Input

The input consists of multiple test cases. Each test case begins with a line containing two non-negative integers M and N. Then N lines follow, each contains two non-negative integers J[i] and F[i] respectively. The last test case is followed by two -1's. All integers are not greater than 1000.

Output

For each test case, print in a single line a real number accurate up to 3 decimal places, which is the maximum amount of JavaBeans that FatMouse can obtain.

Sample Input

5 3
7 2
4 3
5 2
20 3
25 18
24 15
15 10
-1 -1

Sample Output

13.333
31.500

题目大意

分析

  • 每次兑换咖啡豆时,优先兑换性价比最高的。
  • 若猫粮耗尽,则计算结束,即可输出最优解。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>

using  namespace  std;

const int MAXN = 1000;

struct JavaBean{
    double weight;
    double cost;
};

JavaBean arr[MAXN];

//降序比较函数
bool Compare(JavaBean x,JavaBean y){
    return (x.weight / x.cost) > (y.weight/y.cost);
}

int main(){
    int m,n;
    while(scanf("%d%d",&m, &n) != EOF){
        if(m == -1 && n == -1){
            break;
        }
        for(int i = 0; i<n; ++i){
            scanf("%lf%lf", &arr[i].weight, &arr[i].cost);
        }
        sort(arr,arr+n,Compare);
        double answer = 0;
        for(int i = 0; i<n; ++i){
            if(m >= arr[i].cost){ //能全部买下第i个房间的咖啡豆
                m -= arr[i].cost;
                answer +=arr[i].weight;
            } else{               //只能买下房间内的部分咖啡豆,就耗光了猫粮
                answer += arr[i].weight*(m / arr[i].cost);
                break;
            }
        }
        printf("%.3f\n",answer);
    }
    return 0;
}

HDU:Senior's Gun

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 3142 Accepted Submission(s): 1029

Problem Description

Xuejiejie is a beautiful and charming sharpshooter.

She often carries n guns, and every gun has an attack power a[i].

One day, Xuejiejie goes outside and comes across m monsters, and every monster has a defensive power b[j].

Xuejiejie can use the gun i to kill the monster j, which satisfies b[j]≤a[i], and then she will get a[i]−b[j] bonus .

Remember that every gun can be used to kill at most one monster, and obviously every monster can be killed at most once.

Xuejiejie wants to gain most of the bonus. It's no need for her to kill all monsters

Input

In the first line there is an integer T, indicates the number of test cases.

In each case:

The first line contains two integers n, m.

The second line contains n integers, which means every gun's attack power.

The third line contains m integers, which mean every monster's defensive power.

\(1≤n,m≤100000, −10^9≤a[i], b[j]≤10^9\)

Output

For each test case, output one integer which means the maximum of the bonus Xuejiejie could gain.

Sample Input

1
2 2
2 3
2 2

Sample Output

1

分析

  • 优先考虑\(a[i]-b[i]\)最大的情况

代码

#include <iostream>
#include <cstdio>
#include <algorithm>

using  namespace std;

const int MAXN = 100001;

long long gun[MAXN];
long long monster[MAXN];

//降序比较函数
bool Compare(long  long x, long long  y){
    return x > y;
}

int main(){
    int caseNumber;
    scanf("%d",&caseNumber);
    while (caseNumber--){
        int n, m;
        scanf("%d %d", &n,&m);
        for(int i = 0; i < n; ++i){
            scanf("%lld", &gun[i]);
        }
        for(int i = 0; i < m; ++i){
            scanf("%lld", &monster[i]);
        }
        sort(gun,gun + n,Compare); //枪从大到小排序
        sort(monster,monster + m); //怪物从小到大排序
        long  long answer = 0;
        for(int i = 0; i < n; ++i){
            if(i >= m || gun[i] <= monster[i]){
                break;
            }
            answer += (gun[i]-monster[i]);
        }
        printf("%lld\n",answer);
    }
    return 0;
}

KY4 代理服务器

时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M

本题知识点: 贪心 二分

题目描述

使用代理服务器能够在一定程度上隐藏客户端信息,从而保护用户在互联网上的隐私。我们知道n个代理服务器的IP地址,现在要用它们去访问m个服务器。这 m 个服务器的 IP 地址和访问顺序也已经给出。系统在同一时刻只能使用一个代理服务器,并要求不能用代理服务器去访问和它 IP地址相同的服务器(不然客户端信息很有可能就会被泄露)。在这样的条件下,找到一种使用代理服务器的方案,使得代理服务器切换的次数尽可能得少。

输入描述:

每个测试数据包括 n + m + 2 行。

第 1 行只包含一个整数 n,表示代理服务器的个数。
第 2 行至第n + 1行每行是一个字符串,表示代理服务器的 IP地址。这n个 IP地址两两不相同。
第 n + 2 行只包含一个整数 m,表示要访问的服务器的个数。
第 n + 3 行至第 n + m + 2 行每行是一个字符串,表示要访问的服务器的 IP 地址,按照访问的顺序给出。
每个字符串都是合法的IP地址,形式为“xxx.yyy.zzz.www”,其中任何一部分均是0–255之间的整数。输入数据的任何一行都不包含空格字符。
其中,1<=n<=1000,1<=m<=5000。

输出描述:

可能有多组测试数据,对于每组输入数据, 输出数据只有一行,包含一个整数s,表示按照要求访问服务器的过程中切换代理服务器的最少次数。第一次使用的代理服务器不计入切换次数中。若没有符合要求的安排方式,则输出-1。

输入

3
166.111.4.100
162.105.131.113
202.112.128.69
6
72.14.235.104
166.111.4.100
207.46.19.190
202.112.128.69
162.105.131.113
118.214.226.52

输出

1

代码

#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;
map<string, bool> agent;
void reset()
{
    for (auto it = agent.begin(); it != agent.end(); it++)
        (*it).second = true;
}
bool all_false()
{
    for (auto it = agent.begin(); it != agent.end(); it++)
        if ((*it).second == true)
            return false;
    return true;
}
int main()
{
    int n, m;
    while (cin >> n)
    {
        string tmp;
        for (int i = 0; i < n; i++)
        {
            cin >> tmp;
            agent[tmp] = true;
        }
        cin >> m;
        int cnt = 0;
        for (int i = 0; i < m; i++)
        {
            cin >> tmp;
            if (agent.count(tmp) == 1)
            {
                agent[tmp] = false;
                if (all_false() == true)
                {
                    reset();
                    agent[tmp] = false;
                    cnt++;
                }
            }
        }
        if (agent.size() == 1 && cnt != 0)
            cout << -1 << endl;
        else
            cout << cnt << endl;
    }
}

2.区间贪心

  • 区间贪心是指当由多个不同的区间存在,且这些区间有可能相互重叠的时候,如何选择 才能从众多的区间中,选取最多两两互不相交的区间。

ACM:今年暑假不AC

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 108924 Accepted Submission(s): 58038

Problem Description

“今年暑假不AC?”
“是的。”
“那你干什么呢?”
“看世界杯呀,笨蛋!”
“@#$%^&*%...”

确实如此,世界杯来了,球迷的节日也来了,估计很多ACMer也会抛开电脑,奔向电视了。
作为球迷,一定想看尽量多的完整的比赛,当然,作为新时代的好青年,你一定还会看一些其它的节目,比如新闻联播(永远不要忘记关心国家大事)、非常6+7、超级女生,以及王小丫的《开心辞典》等等,假设你已经知道了所有你喜欢看的电视节目的转播时间表,你会合理安排吗?(目标是能看尽量多的完整节目)

Input

输入数据包含多个测试实例,每个测试实例的第一行只有一个整数n(n<=100),表示你喜欢看的节目的总数,然后是n行数据,每行包括两个数据\(Ti_s\),\(Ti_e\) (\(1\le i \le n\)),分别表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。n=0表示输入结束,不做处理。

Output

对于每个测试实例,输出能完整看到的电视节目的个数,每个测试实例的输出占一行。

Sample Input

12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9
0

Sample Output

5

分析

首先思考:第一个节目选什么?

  • 选择开始时间最早的
  • 选择持续时间最短的
  • 选择结束时间最早的

答案是选择结束时间最短的,保证看完节目后,剩余的时间最大化。不断重复选择当前结束时间最早的,可以得到最优解。

因此本题先把节目按照结束时间的早晚进行排序,然后再判断能否完整观看节目(及比较当前时间是否小于等于节目开始时间)。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>

using  namespace std;

struct Program{
    int startTime;
    int endTime;
};

const int MAXN = 1000;

Program arr[MAXN];

//结束时间升序排序的比较函数
bool Compare(Program x, Program y){
    return x.endTime < y.endTime;
}

int main(){
    int n;
    while (scanf("%d", &n) != EOF){
        if(n == 0){
            break;
        }
        for(int i = 0; i<n; i++){
            scanf("%d%d",&arr[i].startTime,&arr[i].endTime);
        }
        sort(arr, arr+n, Compare);
        int currentTime = 0;
        int answer = 0;
        for(int i = 0; i < n; i++){
            if(currentTime <= arr[i].startTime){
                currentTime = arr[i].endTime;
                answer++;
            }
        }
        printf("%d\n", answer);
    }
}

CodeForces:Case of Fugitive

Problem Description

Andrewid the Android is a galaxy-famous detective. He is now chasing a criminal hiding on the planet Oxa-5, the planet almost fully covered with water.

The only dry land there is an archipelago of \(n\) narrow islands located in a row. For more comfort let's represent them as non-intersecting segments on a straight line: island \(i\) has coordinates \([l_i , r_i ]\), besides, \(r_i < l_i + 1\) for \(1 ≤ i ≤ n - 1\).

To reach the goal, Andrewid needs to place a bridge between each pair of adjacent islands. A bridge of length \(a\) can be placed between the \(i\) -th and the \((i + 1)-th\) islads, if there are such coordinates of \(x\) and \(y\) , that $l_i ≤ x ≤ r_i $, \(l_i + 1 ≤ y ≤ r_i + 1\) and \(y - x= a\) .

The detective was supplied with \(m\) bridges, each bridge can be used at most once. Help him determine whether the bridges he got are enough to connect each pair of adjacent islands.

Input

The first line contains integers n$ (2 ≤ n ≤ 2·10^5) $and m \((1 ≤ m ≤ 2·10^5)\)

— the number of islands and bridges.

Next n lines each contain two integers \(l_i\) and \(r_i\) \((1 ≤ l_i ≤ r_i ≤ 10^{18})\)

— the coordinates of the island endpoints.

The last line contains minteger numbers \(a_1, a_2, ..., a_m\) $ (1 ≤ a_i ≤ 10^{18}) $

— the lengths of the bridges that Andrewid got.

Output

If it is impossible to place a bridge between each pair of adjacent islands in the required manner, print on a single line "No" (without the quotes), otherwise print in the first line "Yes" (without the quotes), and in the second line print n - 1 numbers \(b_1, b_2, ..., b_{n - 1}\), which mean that between islands i and i + 1 there must be used a bridge number \(b_i\).

If there are multiple correct answers, print any of them. Note that in this problem it is necessary to print "Yes" and "No" in correct case.

Examples

Input

4 4
1 4
7 8
9 10
12 14
4 5 3 8

Output

Yes
2 3 1 

Input

2 2
11 14
17 18
2 9

Output

No

Input

2 1
1 1
1000000000000000000 1000000000000000000
999999999999999999

Output

Yes
1 

Note

In the first sample test you can, for example, place the second bridge between points 3 and 8, place the third bridge between points 7 and 10 and place the first bridge between points 10 and 14.

In the second sample test the first bridge is too short and the second bridge is too long, so the solution doesn't exist.

题目大意

按照的安德鲁是一位著名的银河侦探。他现在正在追捕一名隐藏在Oxa-5行星上的罪犯,这颗行星几乎完全被水覆盖。

唯一的旱地是一个由n个狭窄岛屿组成的群岛。为了更加便于表述,将它们表示为直线上不相交的线段:岛屿的坐标为\([l_i,r_i]\),对于所有\(2\le i \le n-1\)都有\(r_i\lt l_i +1\)

为了达到目标,安德鲁需要在每对相邻岛屿之间驾一座桥。长度为a的桥可以驾在第i和第i+1号岛屿之间,如果它们的坐标为x和y,则有\(l_i\le x\le r_i\)\(l_i+1 \le y \le r_i+1\)\(y-x= a\)

侦探可以用m座桥,每座桥最多可以使用一次。请帮助他确定他可用的桥梁是否能够连接每对相邻的岛屿。

分析

  • 在岛屿与岛屿之间架设桥梁是有条件的,比如第一个岛屿为‘“1 4”, 第二个岛屿为“7 8”,那么桥梁最短必须为7-4=3,最长不能超过8-1=7
  • 对于每个区间,设置最大值maxmux与最小值minimum,并对区间按照最小值minimum进行排序,再对桥的长度length进行排序
  • 按照桥从小到大的方式来选择搭建的区间,在所有的\(minimum \le length \le maxmum\) 的区间中,选择maxmum最小的那个作为该桥搭建的区间即可。
  • 本题还涉及到优先队列。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int MAXN = 200001;

struct Island{
    long long left;
    long long right;
};

struct Bridge{
    long long length;
    long long index;
};

struct Interval{
    long long minimum;
    long long maxmum;
    long long index;
    bool operator< (Interval x) const {
        return maxmum > minimum;
    }
};

bool IntervalCompare(Interval x,Interval y){
    if(x.minimum == y.minimum){
        return x.maxmum < y.maxmum;
    } else {
        return x.minimum < y.minimum;
    }
}

bool BridgeCompare(Bridge x,Bridge y){
    return x.length < y.length;
}

Island island[MAXN];
Bridge bridge[MAXN];
Interval interval[MAXN];
long long answer[MAXN];

bool Solve(int n, int m){
    priority_queue<Interval> myQueue;
    int position = 0;
    int number = 0;
    for(int i = 0; i < m; ++i){
        while(myQueue.top().maxmum < bridge[i].length && !myQueue.empty()){
            myQueue.pop();
        }
        while (position < n-1 && interval[position].minimum <= bridge[position].length
        && interval[position].maxmum >= bridge[position].length){
            myQueue.push(interval[position]);
            position++;
        }
        if(!myQueue.empty()) {
            Interval current = myQueue.top();
            myQueue.pop();
            answer[current.index] = bridge[i].index;
            number++;
        }
    }
    return number == n-1;
}

int main(){
    int n, m;
    while (scanf("%d%d",&n, &m) != EOF){
        memset(island, 0, sizeof(island));
        memset(bridge, 0, sizeof(bridge));
        memset(interval, 0, sizeof(interval));
        memset(answer, 0, sizeof(answer));
        for(int i = 0; i < n; ++i){
            scanf("%lld%lld", &island[i].left, &island[i].right);
        }
        for(int i = 0; i < m; ++i){
            scanf("%lld", &bridge[i].length);
            bridge[i].index = i+1;
        }
        for(int i = 0; i < n-1; ++i){
            interval[i].minimum = island[i+1].left - island[i].right;
            interval[i].maxmum = island[i+1].right - island[i].left;
            interval[i].index = i;
        }
        sort(interval,interval+n-1,IntervalCompare);
        sort(bridge,bridge+m,BridgeCompare);
        if(Solve(n,m)){
            printf("Yes\n");
            for(int i=0; i < n - 1; ++i){
                printf("%lld\n",answer[i]);
            }
        } else {
            printf("No\n");
        }
    }
    return 0;
}

PAT:To Fill or Not to Fill

Problem Description

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 }(\leq 100),\) the maximum capacity of the tank; \(D(\leq 30000)\), the distance between Hangzhou and the destination city; \(D_{\text {avg }}(\leq 20)\), the average distance per unit gas that the car can run; and \(N(\leq 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}(\leq D)\), the distance between this station and Hangzhou, for \(i=1, \cdots, 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

题目大意

分析

转载自:https://www.cnblogs.com/XBWer/p/3866486.html

以后再学吧,最后这两道题也太难了!

代码

/*
分析:
      很明显,这道题的思路是贪心,也就是要用尽可能少的钱,要去尽可能远的地方。
      那么,像这种贪心的题目,怎么去思考呢?
      首先,今天听到戴龙翱(ZJU大牛)提到的一点,对于贪心的题目,测试样例,必须自己去体会一遍,这样,很有可能会给我们带来不少启发。
      那么,我们就一起来过一遍测试样例吧:

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

先画个图再看
1:起点,肯定是要加油的,不然跑都跑不起来,那么,问题来了——加多少?
让我们先来观察一下,油箱加满之后,最远能跑600;也就是说,如果我现在在起点把油箱加满的话,[0,600]都是我可以达到的路程范围;
好了,那么,我们只需要看这个范围内的如何做出贪心策略;
起点处的油价是7.1,如果之后遇到的第一个加油站A油价小于7.1,那么,在A之后不管是什么情况,我们都应该加油(至于要加多少,还不能确定),
因为至少在当前状态下,这样做是最“贪婪”的。
2:通过1的分析,我们选择了加油站B。而且值得强调的是,我们在起点A加的油跑到B时是正好用完的状态。
这时,我们站在了B点,B点最远能到750(150+600),我们又如何根据贪心算法来做出贪婪的决策呢?
B处的油价是7,显然,750之前的加油站有很多,油价有比7小的,比7大的,也有等于7的。那么,贪婪的你,一定不会傻到去选择一个油价贵的(如C、E)
因为我完全可以到达比7小的加油站再加油,为什么要去比7大的加油站加油呢?
so,我们选择了D(油价6.85),而且,D的油价比当前的便宜,所以我们加油只要够从B——>D就好,加多了就不符合我贪婪的本性了!
3:到了D之后,可以说是比较开心的,因为在他[300,300+600]的范围内这价是最便宜的,此时不加更待何时!?因为是最便宜的,所以,为了贪,必须加满!
加满了之后,最远可以到900(300+600),那么,在900之前,我们会遇到E、F,且F油价较E要便宜一些,因此,为了到达目的地,我们不得不到F加油。
4:和之前的情况有所不同的是,这次,我们到目的地的时候,还是有油剩余的(600-300<600),而且剩余的油够跑300(即可以跑到900)。
那么,我们在F加多少的油呢?
站在F的位置,我们开始思考。距离400有一个加油站G,可是油价要7.3,坑爹呢!这么贵!
可是,就算F加满了,我们也只能跑到1200(600+600),所以,没办法,为了到达目的地,我们不得不到G加,但是,这里要注意,因为G比F的油价要贵,
所以,为了贪,我们会在F把油加满,(在能够达到目的地的前提下,尽可能在贵的地方少加点油,在便宜的地方多加点油——贪);
5:到了G之后,计算了此时邮箱还剩下的油狗刨200,也就是说,我们在贵的的地方G只需要加50(1250-1000-200),能到H即可,因为H的油价是最便宜(没有之一),
在[1000,1000+600]的范围内,是最便宜的,so,就这样走到了H
6:走到了H之后,就不用多想了,H之后也没有加油站了,而且加满能够到目的地I的油量就够了。

经过了以上分析之后,要开始对以上的各个情况进行抽象,即把遇到的情况分类(要包括所有的情况),并且,基于贪心的思想去考虑不同的情况下,做出何种决策
处在当前加油站(起点加油站)的情况下
情况1:600米范围内,有目的地——计算恰好能到目的地的油量                                                                  【6】
情况2:600米范围内没有加油站,无论油价多贵——加满——能跑多远算多远
情况3:600米范围内有加油站:
                            a:有比当前加油站的油价更便宜的加油站——加到恰好能到那个最近的油站的油量              【1】【2】【5】
                            (注:1、如果有多个便宜的,还是要先选取最近的那个,而不是最便宜的那个;2、可能还有油剩余)
                            b:没有比当前加油站的油价更便宜的加油站——加满,然后在600范围内找到最便宜的加油站加油             【3】【4】

再来看第二组数据:
Sample Input 2:
50 1300 12 2
7.10 0
7.00 600

Sample Output 2:
The maximum travel distance = 1200.00

分析过程:
1:600的范围内(包括600),有加油站,而且比当前的油价要便宜,因此,属于情况3—a,故,我们加到恰好能到,这里比较特殊的是,只有加满才恰好能到,
注意,这里不能归为情况2,因为情况2的结果对应着一定无法到达目的地,所以,当前的状态还无法判断出能不能到达目的地;
2:600范围内,没有加油站,这里属于情况2,能跑多远跑多远,因为已经无法到达目的地了,只能尽可能地跑更远

经过以上的分析,就可以开始尝试地写代码了
特殊的情况优化:
    1:起点没有加油站
    2:起点即终点

主要的几个关键点,或者说是行驶的过程中需要记录些什么信息:
    1:到达当前加油站的油量——因为,你要计算还需要加多少油,所以,总共需要的油量—现有的油量=在当前加油站要加的油量
 */
#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;

typedef struct
{
    double pos;
    double price;
}gasstation;
gasstation gasst[502];

bool cmp(gasstation a,gasstation b)
{
    if(a.pos<b.pos)
        return true;
    return false;
}

int main()
{
    double Cmax,D,Davg;
    int N;
    scanf("%lf%lf%lf%d",&Cmax,&D,&Davg,&N);
    int i;
    for(i=0;i<N;i++)
        scanf("%lf%lf",&gasst[i].price,&gasst[i].pos);
    sort(gasst,gasst+N,cmp);
    if(D==0)
    {
        printf("0.00\n");
        return 0;
    }
    if(gasst[0].pos!=0)
    {
        printf("The maximum travel distance = 0.00\n");
        return 0;
    }
    int curstnum=0;               //当前所处的油站编号,即当前的位置
    double curgas=0;              //当前的油量
    double curcost=0;                //当前的花费
    bool flag=false;              //是否达到目的
    double maxrundis=Cmax*Davg;        //邮箱加满最远能行驶的距离
    while(!flag)
    {
        bool tag=false;            //最大距离内是否有加油站
        bool ifcheaper=false;    //是否有便宜的
        double cheapestprice=10000;    //找出最便宜的
        int cheapestnum;        //没有更便宜的情况下,找出最便宜的
        for(i=curstnum+1;i<N;i++)
        {
            if((gasst[i].pos-gasst[curstnum].pos)<=maxrundis)    //范围内
            {
                tag=true;         //有加油站
                if(gasst[i].price<gasst[curstnum].price)        //情况3-a
                {                                            //且有更便宜的
                    ifcheaper=true;
                    double dist=gasst[i].pos-gasst[curstnum].pos;
                    double needgas=dist/Davg-curgas;
                    curgas=0;
                    curcost+=(needgas*gasst[curstnum].price);
                    curstnum=i;
                    break;
                }
                if(gasst[i].price<cheapestprice)
                {
                    cheapestprice=gasst[i].price;
                    cheapestnum=i;
                }
            }
            else
                break;
        }
        if(!ifcheaper&&(maxrundis>=(D-gasst[curstnum].pos)))   //说明已经可以到达目的地了,情况1
        {
            double dist=D-gasst[curstnum].pos;
            double needgas=dist/Davg-curgas;
            curcost+=needgas*gasst[curstnum].price;
            printf("%.2lf\n",curcost);
            return 0;
        }
        if(tag&&!ifcheaper)            //情况3-b
        {
            double needgas=Cmax-curgas;
            curcost+=(needgas*gasst[curstnum].price);
            double dist=gasst[cheapestnum].pos-gasst[curstnum].pos;
            curgas=Cmax-dist/Davg;
            curstnum=cheapestnum;
        }
        else if(!tag)                        //情况2
        {
            printf("The maximum travel distance = %.2lf\n",gasst[curstnum].pos+maxrundis);
            return 0;
        }
    }
    return 0;
}
posted @ 2021-01-30 23:12  张吱吱  阅读(484)  评论(0编辑  收藏  举报