小学奥数之称球问题

题目描述

给定 \(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;
}
posted @ 2021-04-05 06:34  Rubyonlу  阅读(285)  评论(0编辑  收藏  举报