比赛挑战:"最大限度地参加比赛的数量"

时间:2014-05-28 03:18:43

标签: algorithm

在练习hackerearth的问题时,我遇到了以下问题(不是来自积极的比赛),并且经过多次尝试后未能成功解决问题。

  钱德勒正在参加一场涉及N赛道的比赛   比赛。他想在这些有F量的赛道上驾驶他的旧车   初始燃料。在每场比赛结束时,钱德勒花费了si燃料和   获得一些钱,用来为他的汽车增加燃料量。

     

同样,在任何阶段参加第一场比赛,钱德勒应该有   超过si量的燃料。他也可以参加一次比赛。   帮助钱德勒最大化他可以参加的比赛数量   他可以选择以任何顺序参加特定比赛。

我该如何处理这个问题。我的方法是按(ei-si)进行排序,但我并不能确定燃料存在量超过种族所需的条件。

编辑我尝试使用以下算法解决但是它失败了,我也无法想到任何使算法失败的输入。请帮我弄清楚什么是错误的,或者在算法失败的地方给出一些输入。

Sort (ei-si) in non-increasing order;
start iterating through sorted (ei-si) and find first element such that fuel>=si
   update fuel=fuel+(ei-si);
   update count;
   erase that element from list, and start searching again;

if fuel was not updated than we can't take part in any races so stop searching 
and output count.

编辑以下是我要求的代码。

#include<iostream>
#include<vector>
#include<algorithm>
#include<list>

using namespace std;
struct race{
    int ei;
    int si;
    int earn;


};

bool compareByEarn(const race &a, const race &b)
{
    return a.earn <= b.earn;
}

int main(){

    int t;
    cin>>t;
    while(t--){
        vector<struct race> fuel;
        int f,n;
        cin>>f>>n;
        int si,ei;
        while(n--){
            cin>>si>>ei;
            fuel.push_back({ei,si,ei-si});

        }
        sort(fuel.begin(),fuel.end(),compareByEarn);
        list<struct race> temp;
        std::copy( fuel.rbegin(), fuel.rend(), std::back_inserter(temp ) );


        int count=0;
        while(1){


            int flag=0;
            for (list<struct race>::iterator ci = temp.begin(); ci != temp.end(); ++ci){
                if(ci->si<=f){
                    f+=ci->earn;
                    ci=temp.erase(ci);
                    ++count;
                    flag=1;
                    break;

                }
            }

            if(!flag){
                break;
            }
        }

        cout<<count<<endl;

    }


}

编辑如下面的答案中所述,上述贪婪的方法总是有效。所以现在任何替代方法都是有用的

2 个答案:

答案 0 :(得分:2)

这是我的解决方案,由法官接受:

  1. 消除那些有利润的比赛(ei&gt; si)
  2. 按ei排序(按降序排列)
  3. 使用动态编程算法解决问题。 (它类似于0-1背包的伪多项式解。)
  4. 很明显,您消除有利可图的比赛的顺序无关紧要。 (只要你处理它们直到不能进入更有利可图的比赛。)

    对于其余部分,我将首先证明如果存在解决方案,您可以按ei的降序执行相同的一组比赛,并且解决方案仍然可行。想象一下,我们有一个选择k种族的解决方案,让我们说这些k种族的起始和结束燃料值为s1,...,sk和e1,...,ek。让我成为第一个指数,其中ei&lt; ej(其中j = i + 1)。我们将证明我们可以在不违反任何约束的情况下交换i和i + 1。

    很明显,交换i和i + 1不会破坏i之前或i + 1之后的任何约束,因此我们只需要证明如果我们将其顺序与种族i + 1交换,我们仍然可以执行竞赛i( j)的。在正常的顺序中,如果我们在比赛开始之前的燃料水平我是f,在比赛之后它将是f-si + ei,并且这至少是sj。换句话说,我们具有:f-si + ei> = sj,这意味着f-sj + ei> = si。但是,我们知道ei&lt; ej所以f-sj + ej> = f-sj + ei&gt; = si,因此在第i场比赛之前参加第j场比赛仍将至少为比赛i留下燃料。

    从那里,我们实现了一个动态编程算法,其中d [i] [j]是我们可以参与的最大比赛数,如果我们只能使用比赛i..n并且我们从j单位的燃料开始。

    这是我的代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int maxn = 110;
    const int maxf = 110*1000;
    
    int d[maxn][maxf];
    
    struct Race {
        int s, e;
        bool used;
        inline bool operator < (const Race &o) const {
            return e > o.e;
        }
    } race[maxn];
    
    int main() {
        int t;
        for (cin >> t; t--;) {
            memset(d, 0, sizeof d);
    
            int f, n;
            cin >> f >> n;
    
            for (int i = 0; i < n; i++) {
                cin >> race[i].s >> race[i].e;
                race[i].used = false;
            }
    
            sort(race, race + n);
    
            int count = 0;
    
            bool found;
            do {
                found = 0;
                for (int i = 0; i < n; i++)
                if (!race[i].used && race[i].e >= race[i].s && race[i].s >= f) {
                    race[i].used = true;
                    count++;
                    f += race[i].s - race[i].e;
                    found = true;
                }
            } while (found);
    
    
            for (int i = n - 1; i >= 0; i--) {
                for (int j = 0; j < maxf; j++) {
                    d[i][j] = d[i + 1][j];
                    if (!race[i].used && j >= race[i].s) {
                        int f2 = j - race[i].s + race[i].e;
                        if (f2 < maxf)
                            d[i][j] = max(d[i][j], 1 + d[i + 1][f2]);
                    }
                }
            }
    
            cout << d[0][f] + count << endl;
        }
    
        return 0;
    }
    

答案 1 :(得分:1)

您需要更改compareByEarn函数

bool compareByEarn(const race &a, const race &b)
{
    if(a.earn == b.earn) return a.si < b.si;
    return a.earn < b.earn;
}

以上比较意味着,选择收入更高(或损失更少)的赛道。但是如果有2条相同收益的音轨,则更喜欢需要更多燃料的音轨。

考虑示例

Initially fuel in the car = 4
track 1 : s = 2, e = 1
track 2 : s = 3, e = 2
track 3 : s = 4, e = 3

Expected answer = 3
Received answer = 2 or 3 depending on whether sorting algorithm is stable or unstable and the order of input\.

作为旁注:

  

同样,在任何阶段参加第一场比赛,钱德勒应该有   超过si量的燃料   应转换为

if(ci->si < f){ // and not if(ci->si<=f){

您可以检查我的观察是否正确,或者问题作者是否选择了不正确的句子来描述约束。

编辑
有了更多的推理,我意识到你只能用贪婪的方法做到这一点。
请考虑以下输入。

Initially fuel in the car = 9
track 1 : s = 9, e = 6
track 2 : s = 2, e = 0
track 3 : s = 2, e = 0
track 4 : s = 2, e = 0

Expected answer = 4
Received answer = 3