问题描述
您和您的朋友成功抢劫了一家装满钻石戒指的珠宝店。现在,你们想在你们自己之间分配战利品,但你们都没有贪心。你们都决定在分发戒指时碰碰运气。你的包里有 N 个戒指。你们俩决定一个接一个地轮流挑选一些戒指,直到袋子里没有剩下的戒指。由于您是计划的策划者,因此您将采取第一轮行动。规则是这样的-
-
从包里取出一枚戒指。
-
拿走一半可用的戒指。此规则仅适用于包中环数为偶数的情况。
两个劫匪都会尽量增加他们拥有的戒指数量。如果您和对手都发挥最佳,请找出在分配结束时您可以获得的最大戒指数。
输入
第一行包含一个整数 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;
}