优化黑客算法

时间:2015-08-12 17:57:37

标签: arrays optimization computer-science

我在黑客等级上被问到了这一点,但我还没有找到一个没有分配时间的解决方案。我使用php并且分配的时间是9秒......

这个想法是有#"门票摊位"有一定数量的门票,比如说9,他们卖的任何门票都是以剩下的门票数量为准,所以第一张门票是9美元,第二张是8美元......等等。

你给了两行数据,比如说:

2 4
1 5

第一行包含两个数字:

  1. 摊位数
  2. 售出多少张门票
  3. 第二行包含每个摊位最初有多少张票的列表,所以在这种情况下,摊位1有1张票,摊位2有5张票。

    问题:出售给定票数的最高金额是多少?

    在这种情况下,您以5 + 4 + 3 + 2 = 14美元的价格从摊位2卖出4张票

    那你怎么解决呢我想出了两种方法,两种方式都耗尽了时间

    1. 将档位号(第二行)加载到数组中。通过该阵列N次(销售门票的数量)选择最大数量,将其添加到聚合器,减少该数量。然后你在聚合器中得到总数。

    2. 将档位编号加载到数组中。对数组进行排序。向后遍历数组并执行:存储该数字(当前),将其添加到聚合器,转到下一个值。如果它是相同的(当前),则将其添加到聚合器,从中减去1,继续。如果不同,请返回到数组的末尾并重新开始。做N次(内部循环,而不是外部循环)。

    3. 问题:两个人都没有工作。

      有人能想到更好的解决方案吗?

5 个答案:

答案 0 :(得分:3)

  1. 创建一个包含剩余票数的档位数组。 (所以{0,3,0,2}意味着2个摊位有3张票,3个摊位有1张票)
  2. 递减最高的非零条目,递增其下方的条目,并将该条目的索引添加到您的总数中。
  3. 每售出一张门票,重复#2次。
  4. 还有一种明显的方法可以通过乘以MIN(最高档数,剩余售票数)来改善这一点。

    注意:为了使其表现良好,您的实现语言必须具有真正的数组(即,无论索引如何,都是恒定的访问时间)。我不懂PHP,但是有些语言(JS?)使用顺序列表来模仿数组,但是没有相同的性能。

    以下是上述方法的Java实现(阅读评论以便更好地理解):

            int[] stalls = new int[] { 4, 7, 1, 4, 8, 8 };
            int t = 4;
    
            Arrays.sort(stalls);
    
            int tickets = stalls[stalls.length - 1];
            int[] dp = new int[tickets + 1];
    
            for (int i = 0; i < stalls.length; i++) {
                dp[stalls[i]]++;
            }
    
            int total = 0;
            int i = dp.length - 1;
    
            while (t > 0) {
                if (dp[i] > 0) {
                    total += i;
                    t--;
                    dp[i]--;
                    dp[i - 1]++;
                } else {
                    i--;
                }
            }
    
            System.out.println(total);
    

答案 1 :(得分:2)

如果您担心DP,这是您应该尝试的O(n)解决方案。以下代码仅考虑最大元素,然后继续减少它,直到达到所需的票数。通过将其作为值存储在hashMap中,它可以跟踪乘法因子。最后,如果票数大于所需票数,它将从结果中减去最近添加的票价,直到票数等于所需票数为止。

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class TicketClass {
    public static void main(String[] args){
        int[] arr = {10,10,2,8,6,4};
        int ticket = 23;
        Arrays.sort(arr);
        int mul = 1;
        int k = 0;
        System.out.print("Hello World");
        long res = 0;
        Map<Integer,Integer> hm = new HashMap<>();
        for(int i:arr){
            if(hm.containsKey(i)){
                hm.put(i,hm.get(i)+1);
            }
            else
                hm.put(i,1);
        }

        int curr = arr[arr.length-1];

        while(k < ticket){
            mul=hm.getOrDefault(curr,mul);
            res += curr*mul;
            curr--;
            k += mul;
            if(hm.containsKey(curr))
                hm.put(curr,hm.get(curr)+mul);
        }

//End of while loop
        curr++;
        while(k > ticket){
            k--;
            res -=curr;
        }
        System.out.println("ticket "+k);
        System.out.println(res);
    }
}

答案 2 :(得分:0)

让我们看看为什么你的解决方案会超时。

  

将失速编号(第二行)加载到数组中。通过那个   数组N次(卖出的门票数量)选择最大   编号,将其添加到聚合器,减少该数量。   然后你在聚合器中得到总数。

这是O(N * M),其中N是要售卖的门票数量,M是售票亭的数量。这几乎是一种蛮力的方法,通常不足以击败黑客的测试。

  

将档位号加载到数组中。对数组进行排序。倒退   通过数组和你一样:存储该数字(当前),添加它   到聚合器,转到下一个值。如果它是相同的(当前),   然后将其添加到聚合器,从中减去1,继续。如果是   不同,回到数组的末尾,然后重新开始。做那个N.   时间(内部循环,而不是外部循环)。

也许是我,但我真的不明白你的意思。从我看来,听起来这仍然是O(N * M)。你如何处理大量票数减少很多次的情况,以至于它破坏了你之前的那种?

这里需要的是一个可以有效检索当前最大值的数据结构,可以有效地弹出/插入新元素。看起来最大堆是一个很好的候选者。只需在每个展位中保留最大堆中的可用票数。要通过N展位销售M张票,请N次执行此操作:

  1. 提取最大值 - O(log(M))
  2. 将其添加到结果累加器 - O(1)
  3. 减少它 - O(1)
  4. 将其插入堆中 - O(log(M))
  5. 总体复杂度为O(N*log(M)),这可能是您可以做的最好的。

    C ++实现:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    int max_revenue(vector<int> &tickets, int n) {
    
        make_heap(tickets.begin(), tickets.end());
    
        int res = 0;
        for (int i = 0; i < n; i++) {
            res += tickets.front();
            pop_heap(tickets.begin(), tickets.end());
            tickets[tickets.size()-1]--;
            push_heap(tickets.begin(), tickets.end());
        }
    
        return res;
    }
    
    int main(void) {
        int booths;
        cin >> booths;
        int n;
        cin >> n;
    
        vector<int> tickets(booths);
        for (int i = 0; i < booths; i++)
            cin >> tickets[i];
    
        cout << max_revenue(tickets, n) << endl;
    
        return 0;
    }
    

答案 3 :(得分:0)

对MaxHeap解决方案的优化很少:

您无需逐个删除票证。如果您有一个顶部窗口,您需要知道它有多少票,以及下一张票有多少票。然后,您可以删除差异并在一次操作中计算总价  如果您有多个具有相同最大票数的窗口,只需稍作修改即可。

答案 4 :(得分:0)

这是PHP中的一个实现,这不像其他一些答案那样创建一个额外的数组。总是有很多不同的方法来解决这个问题,看看这个问题,它可能会在未来给你提供想法。有很多方法可以改进实际使用,但这只是为了显示快速解决问题的替代方法

class booth {

public $booths = [];
public $left = 0;
public $count = 0;
public $total = 0;
public $booth = 0;
public $nextBooth = 0;

function __construct() {
    $this->booths = [3, 7, 5, 2, 0, 2, 5, 15, 9, 1, 39, 91, 0, 58, 29];
    $this->left = 25;

    sort($this->booths);

    $this->booth = array_pop($this->booths);


    while($this->left > 0 && count($this->booths) > 0) {
        $this->count++;
        $this->nextBooth = array_pop($this->booths);        
        $this->countTotal($this->booth - $this->nextBooth);
    }

    // found end of array -- clear out any that are left
    if($this->left > 0)
        $this->countTotal(1);

    echo "total: " + $this->total."\r\n";
}

// everything global except loops
function countTotal($loops) {

    for($i = 0; $i < $loops, $this->left > 0; $i++) {
        if ($this->count > $this->left) 
            $this->count = $this->left;

        $this->total += $this->booth * $this->count;
        $this->left -= $this->count;
        $this->booth--;

    }
}

}

new booth();