/*
100楼,2只鸡蛋,要求用最少次数确定鸡蛋可以从多高摔下而不破。
鸡蛋和楼容易被人钻牛角尖,所以换个更普通的命题来讨论下:
有M种“越来越差”的环境(编号1至M)
有N个“完全相同”的样品,样品在第i种环境下的测试结果有两种:
成功 样品完全无损
失败 样品损坏,无法再用
性质1:若样品测试第k种情况时成功,表示所有样品都能够通过环境1至k。
性质2:若样品测试第k种情况时失败,表示所有样品都不能通过环境k至M。
要求用最少的次数,测出探针可承受的最差环境编号。
结合性质1、2,可知每次测试的结果都将把未知区域分成两部分。
假设在用掉了N-1个探针后,成功通过的环境编号最大的为a,失败的环境编号最小为b
只剩最后一个探针,为了完成任务,只能从a依次测试到b
不能跳过中间环境,直接测试后面的,因为一旦失败,就无法确定最差环境。
*/
#include <cassert>
#include <iostream>
#include <cmath>
using namespace std;
int const N = 100;
int g_solve[N + 2][N + 2][N + 2];
int min_test_times(int success, int failure, int probe_count)
{
if(g_solve[success][failure][probe_count] >= 0){
return g_solve[success][failure][probe_count];
}
int unknown_cases = failure - success - 1;
if(unknown_cases <= 1){
//cout << "B\t" << success << '\t' << failure << '\t' << probe_count << '\t' << 1 << endl;
g_solve[success][failure][probe_count] = 1;
return 1;
}
if(probe_count == 1){ // 只有一个探针,只能依次试
//cout << "C\t" << success << '\t' << failure << '\t' << probe_count << '\t' << unknown_cases << endl;
g_solve[success][failure][probe_count] = unknown_cases;
return unknown_cases;
}
int min_times = 0x7FFFFFFF;
for(int step = 1, MAX_STEP = ceil(unknown_cases * 0.5); step <= MAX_STEP; ++step){
int segments = ceil(double(unknown_cases) / step);
int bound = success + step * (segments-1);
int t = 0;
if(segments >= 2){
int a = (segments - 1) + min_test_times(bound - step, bound, probe_count - 1); // 失败
if(a > t){
t = a;
}
}
if(segments >= 1){
int b = (segments - 1) + min_test_times(bound, failure, probe_count); // 成功,probe数量不变
if(b > t){
t = b;
}
}
if(t < min_times){
min_times = t;
}
}
//cout << "D\t" << success << '\t' << failure << '\t' << probe_count << '\t' << min_times << endl;
g_solve[success][failure][probe_count] = min_times;
return min_times;
}
void clearCache()
{
int const UNKNOWN = -1;
for(int i = 0; i < N + 2; ++i){
for(int j = 0; j < N + 2; ++j){
for(int k = 0; k < N + 2; ++k){
g_solve[i][j][k] = UNKNOWN;
}
}
}
}
int main()
{
clearCache();
for(int i = 1; i < 11; ++i){
cout << '\t' << i << "\t" << min_test_times(0, 101, i) << endl;
}
return 0;
}
结果(样品个数、所需试验次数),结果看着“象是正确的”,样品超过二分查找所需的个数后再多也不会有提高了:
1 100
2 18
3 11
4 9
5 8
6 7
7 7
8 7
9 7