小学奥数之称球问题
题目描述
给定 \(n\) 个无标号的球,\(n-1\) 个的重量相同,剩下 \(1\) 个球的重量比这 \(n-1\) 个球略重
给定一个天平,每次可以用天平称量,天平的一边最多放 \(m\) 个球,求最劣情况下找出略重那个球的最小步数
题解
如果天平放的球的个数没有限制
最优决策是将球分成三堆
-
\(3n\),直接分成 \(n, n, n\) 就行了
-
\(3n + 1\),分成 \(n, n, n+1\),用天平称 \(n,n\) 这两堆,最劣的情况是那个特殊球在 \(n+1\) 里
-
\(3n + 2\),分成 \(n, n+1, n+1\),用天平称 \(n+1,n+1\) 这两堆,最劣的情况就是那个特殊球在这两个 \(n+1\) 里
如果天平有限制
最先会限制到上面的第三种情况,即 \(n+1>m\),总球数 \(3n+2>3m-1\),显然这时只能拿出两个 \(m\) 去称,最劣的情况是剩下没称的那一堆,即转移到 \(f(n-2m)\),这个显然可以 \(\Theta(1)\) 转移
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define rint register int
#define rll register long long
using namespace std;
const int maxn = 1e5 + 50, INF = 0x3f3f3f3f;
inline ll read () {
rll x = 0, w = 1;
char ch = getchar ();
for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
return x * w;
}
ll n, m;
inline ll DFS (ll n) {
if (n == 1 || n == 0) return 0;
if (n >= 3 * m + 2) {
rll res = (n - m - 2) / (2 * m);
return DFS (n - 2 * res * m) + res;
} else {
if (n % 3 == 0) return DFS (n / 3) + 1;
else if (n % 3 == 1) return DFS (n / 3 + 1) + 1;
else return DFS (n / 3 + 1) + 1;
}
}
int main () {
n = read(), m = read();
printf ("%lld\n", DFS (n));
return 0;
}