从袋子中拣货的时间高效算法

问题描述

您和您的朋友成功抢劫了一家装满钻石戒指的珠宝店。现在,你们想在你们自己之间分配战利品,但你们都没有贪心。你们都决定在分发戒指时碰碰运气。你的包里有 N 个戒指。你们俩决定一个一个地轮流挑选一些戒指,直到袋子里没有剩下的戒指。由于您是计划的策划者,因此您将采取第一轮行动。规则是这样的-

  1. 从包里取出一枚戒指。

  2. 拿走一半可用的戒指。此规则仅适用于包中环数为偶数的情况。

两个劫匪都会尽量增加他们拥有的戒指数量。如果您和对手都发挥最佳,请找出在分配结束时您可以获得的最大戒指数。

输入

第一行包含一个整数 N(1 ≤ N ≤ 1000000) 表示包中的环数。

输出

您可以获得的最大戒指数量

样本输入

6

样本输出 4

陷入这个问题,任何人都可以帮助我,任何语言都最好使用 Python。

解决方法

这是一个 O(logn) 的解决方案。让f(n)解决方案

很容易检查:

- if (n odd) f(n) = n - f(n-1)
- if (n even) f(n) = max(n - f(n-1),n - f(n/2))

解释:在 n 是奇数中,唯一的可能性是选择一个环,如果 n 是偶数,我们可以选择,选择一个环或一半的数据包。

这些关系允许一个简单的迭代解决方案,复杂度为 O(n)。

但是,通过注意以下内容,可以得到复杂度为 O(logn) 的递归解决方案:

  • if n == 4k+2 -> 最好取一半的戒指。下一步,对方只能拿一个戒指
  • if n == 4k (and n > 4) -> 最好只带一个戒指。那么,另一个只能拿一个,然后,我们到达前一个有利的情况

这是一个简单的代码来说明这个算法。

它比较了 O(n) 迭代解决方案和 O(logn) 递归解决方案的结果。


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

//  Iterative O(n) solution
int rings_iterative (int n) {
    std::vector<int> f (n+1);
    f[0] = 0;
    for (int i = 1; i <= n; ++i) {
        if (i%2) {
            f[i] = i - f[i-1];
        } else {
            f[i] = std::max(i - f[i/2],i - f[i-1]);
        }
    }
    return f[n];
}

//  Recursive O(logn) solution
int rings_recursive (int n) {
    //std::cout << "n = " << n << std::endl;
    if (n == 0) return 0;
    if (n%2) return n - rings_recursive (n-1);
    if (((n/2) % 2) || (n <= 4)) {      // n = 4k+2 -> divide by 2
        return n - rings_recursive(n/2);
    } else {                // n = 4k -> take one ring only
        return 1 + rings_recursive(n-2);
    }
    return -1;
}

int main () {
    int n = 999876;
    int ring1,ring2;
    ring1 = rings_iterative (n);
    ring2 = rings_recursive (n);
    
    std::cout << "n = " << n << " -> " << ring1 << " and " << ring2 << std::endl;
        
    int n_min = 643831;
    int n_max = 653875; 
    for (n = n_min; n <= n_max; ++n) {
        ring1 = rings_iterative (n);
        ring2 = rings_recursive (n);
        if (ring1 != ring2) {
            std::cout << "n = " << n << " -> " << ring1 << " and " << ring2 << std::endl;
            return 1;
        }
    }
    std::cout << "No difference\n";
    return 0;
}