小学奥数之称球问题

题目描述

给定 (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
", DFS (n));
	return 0;
}
原文地址:https://www.cnblogs.com/Rubyonly233/p/14617740.html