摘苹果树

时间:2010-04-08 22:30:25

标签: c++ c algorithm

我有以下问题:我给了一棵有N个苹果的树,每个苹果给我它的重量和高度。我可以摘苹果到达给定的高度H,每次我摘苹果时,每个苹果的高度都会随着U而增加。我必须找出我能挑选的苹果的最大重量。

1≤N≤100000
0< {H,U,苹果的重量和高度,最大重量}< 2 31

示例:

N=4  H=100  U=10  

height weight  
  82     30
  91     10
  93      5
  94     15

答案是45:首先选择重量为15的苹果,然后选择重量为30的苹果。

有人可以帮我解决这个问题吗?

6 个答案:

答案 0 :(得分:3)

倒退。首先确定你要选择的最后一个苹果,然后是倒数第二个,等等。

import heapq

def solve(apples, H, U):
  """Solve apple-picking problem.

  apples must be a sequence of (H, W) pairs.
  """
  if U == 0:
    return sum(x[1] for x in apples if x[0] <= H)

  apples = sorted(apples, reversed=True)
  # also creates a copy, to not modify caller's data

  picked_weight = 0
  available_weights = []  # stored negative for heapq

  offset = U - H % U
  if offset == U: offset = 0
  top = offset - U

  while (apples or available_weights) and top <= H:
    if available_weights:
      picked_weight += -heapq.heappop(available_weights)
      top += U
    else:
      top += U * max(1, (apples[-1][0] - top) // U)

    while apples and apples[0][0] <= top:
      heapq.heappush(available_weights, -apples.pop()[1])

  return picked_weight

简单测试:

def test(expected, apples, H, U):
  actual = solve(apples, H, U)
  if expected != actual:
    print "expected=%r actual=%r | H=%r U=%r apples=%r" % (
              expected,   actual,     H,   U,   apples)

test(45, [(82, 30), (91, 10), (93,  5), (94, 15)], 100, 10)
test(22, [( 1,  1), ( 2,  1), (81, 10), (82, 10)], 100, 10)
test(20, [( 1, 10), ( 2, 10), (11,  1)], 20, 10)
test(20, [(81, 10), (82, 10), (90,  5)], 100, 10)
test(1, [(2**31 - 1, 1)], 2**31 - 1, 1)

有人要求C ++,所以在这里。它与上述Python的代码和逻辑几乎相同,除了一个变化:C ++ stdlib的堆函数使用最大值而不是min,因此不需要否定。 (我保留了这个自包含,但heap adaptercontainer inserter等实用程序将使代码更易于使用。)

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>


struct Apple {
  int h, w;

  friend bool operator<(Apple const& a, Apple const& b) {
    return a.h < b.h or (a.h == b.h and a.w < b.w);
  }
  friend bool operator>(Apple const& a, Apple const& b) {
    return b < a;
  }
  friend std::ostream& operator<<(std::ostream& s, Apple const& v) {
    s << '(' << v.h << ", " << v.w << ')';
    return s;
  }
};

template<class T, class C, T C::*M>
struct sum {
  T operator()(T const& cur, C const& next) { return cur + next.*M; }
};

int solve(std::vector<Apple> apples, int H, int U) {
  if (U == 0) {
    return std::accumulate(apples.begin(), apples.end(), 0, sum<int, Apple, &Apple::w>());
  }

  std::sort(apples.begin(), apples.end(), std::greater<Apple>());

  int picked_weight = 0;
  std::vector<int> available_weights;  // NOT stored negative, unlike Python code

  int offset = U - H % U;
  if (offset == U) offset = 0;
  int top = offset - U;

  while ((apples.size() or available_weights.size()) and top <= H) {
    if (available_weights.size()) {
      picked_weight += available_weights.front();
      std::pop_heap(available_weights.begin(), available_weights.end());
      available_weights.pop_back();
      top += U;
    }
    else {
      top += U * std::max(1, (apples.back().h - top) / U);
    }

    while (apples.size() and apples.back().h <= top) {
      available_weights.push_back(apples.back().w);
      std::push_heap(available_weights.begin(), available_weights.end());
      apples.pop_back();
    }
  }

  return picked_weight;
}

C ++测试:

template<int N>
void test(int expected, Apple (&apples)[N], int H, int U) {
  std::vector<Apple> v (apples, apples + N);
  int actual = solve(v, H, U);
  if (expected != actual) {
    std::printf("expected=%d actual=%d | H=%d U=%d apples=[",
                    expected,   actual,     H,   U);
    std::vector<Apple>::const_iterator i = v.begin();
    if (i != v.end()) {
      std::cout << *i;
      for (++i; i != v.end(); ++i) {
        std::cout << ", " << *i;
      }
    }
    std::cout << "]" << std::endl;
  }
}

int main() {
  {
    Apple data[] = {{82, 30}, {91, 10}, {93,  5}, {94, 15}};
    test(45, data, 100, 10);
  }
  {
    Apple data[] = {{ 1,  1}, { 2,  1}, {81, 10}, {82, 10}};
    test(22, data, 100, 10);
  }
  {
    Apple data[] = {{ 1, 10}, { 2, 10}, {11,  1}};
    test(20, data, 20, 10);
  }
  {
    Apple data[] = {{81, 10}, {82, 10}, {90,  5}};
    test(20, data, 100, 10);
  }
  {
    int n = 2147483647; // 2**31 - 1
    Apple data[] = {{n, 1}};
    test(1, data, n, 1);
  }
}

答案 1 :(得分:1)

要解决这个问题首先要注意的是,你应该按照高度递减的顺序挑选苹果。如果你要选择苹果A和B,A高于B,那么首先选择B是没有意义的,因为它可能会推动A太高;另一方面,首先选择A将增加B,但不会增加到大于A + U的高度。两种情况下的最终结果都相同,但首先选择B可能会消除选择A的机会。

要做的第一件事是按降序排序苹果(即从最高到最低)。

接下来,设计一个问题的递归解决方案(忽略复杂性)。看着第一个苹果,你必须决定“服用它还是离开它会更好吗?”所以解决方案基本上是:

max(拿第一个苹果不要先拿苹果)。

你可以为第二个苹果,第三个苹果等减少这个。

这应该为您提供参数看到的苹果数量采摘苹果数量的功能。这使您的函数输入空间大小为O( N 2 )。从那里,只记住你的输入,你有一个时间复杂度为O( N 2 )的算法。

答案 2 :(得分:0)

好的,你知道你的最大身高,而且当你从树上摘下东西时,你知道一切都会向上移动。基本上顶部和顶部之间的所有东西 - 你从树上摘下一个苹果后将无法使用,所以也许你的第一个选择应该来自那个集合?

编辑当你沿着树行进时,在每个位置采取最重的,然后检查其他候选人与你已经选择的那些;如果有任何比你选择的重,请交换。

(是的,自从提出这个想法后,这将更容易自下而上。)

答案 3 :(得分:0)

1)确定每个苹果,n,它的寿命有多长 - 即。选择的数量,Pn,它将保留在树上。

2)从具有最大Pn的苹果中挑出最重的。减少该组中每个剩余苹果的Pn。

3)重复步骤2直到所有苹果都有Pn = 0。

答案 4 :(得分:0)

这是一个很大的提示:

通过降低高度来订购苹果。

考虑所有N个苹果都是不同高度的情况。这是微不足道的,然后在逐渐降低的高度逐个挑选苹果,产生最佳解决方案(问问自己为什么。)

如果两个或更多苹果处于同一高度,则出现此问题的唯一困难。事实上,当你在连续的苹果高度之间存在差距时,就会出现唯一的真正的难度。例如,假设您在苹果开始无法触及之前有t个时间步长。这意味着在这个确切的时刻,您可以优先处理最高组t步中的所有苹果。这是因为在树开始反击之前你有t个“免费赠品”选择。

这是一张图片

  ------------- H
  _
  |
  [t]
  |
  _
  A A A A ... A <-closest group to H
  ._
  .|
  .[t]      all apples in this range should be treated with same priority
  .|        
  ._     

答案 5 :(得分:-2)

在我看来,你不需要动态编程解决方案,只需要一个贪婪的算法。按高度对苹果进行分类,从高度在100到100之间的苹果中挑选最重的苹果 - 然后将所有这些苹果移除至100-U并重复90-U(或通过U增加所有苹果的重量)

因为你总是可以进一步捡苹果,所以我们的想法是挑选最重的苹果,这些苹果首先是不可及的。

这应该有效,因为高度U的增加是恒定的。如果它不是常数,那么你必须结合memoization使用递归公式,这是动态编程。