用户随机选一层i,扔下去,有两个结果:
碎掉,或者没有碎掉。
1.如果碎掉,去试验前i-1层,那么最多需要i-1次再加上碎掉的那一次,最多就是i次。
2.如果没有碎掉,等于说从第i+1层往后,也还是有两个弹珠,这就回到了递归的思路上了。
在这种没有碎掉的情况下,假如总共有N层,那么N层的最多次数等于N-i层的最多次数。所以
针对用户随机选择的这一层i, 以上1和2两种情况, 求其最大值就是最差情况下的次数。
用户随机选择的这个i,也可以变化的,取值 1,2,3...N. 那么我们就会得到不同的值。
针对1,2,3...N. 会有N个,所有这些值中取最小,就是最优解。 用F(N)表示这个最优解,那么F(0)由于没有楼层,所以一定等于0,也就是说不需要实验。
F(0) = 0;
F(N)= min(max(1, F(N-1) + 1), max(2, F(N-2)+ 1)... max(N, F(0)+ 1))
F(0) = 0;
F(1) = 1;
F (2) = min(max(1, F(1)+ 1), max(2, F(0) + 1)) = 2;
可以使用递归算法求得其值,最终的结果是9次。
package interview;
import java.util.ArrayList;
public class ThrowPinballFrom30Layer {
/**
* 腾讯大厦有39层,你手里有两颗一抹一眼的玻璃珠。当你拿着玻璃珠在某一层往下扔的时候,一定会有两个结果,玻璃珠碎了或者没碎。大厦有个临界楼层。低于它的楼层,往下扔玻璃珠,玻璃珠不会碎,
* 等于或高于它的楼层,扔下玻璃珠,玻璃珠一定会碎。玻璃珠碎了就不能再扔。现在让你设计一种方式,使得在该方式下,最坏的情况扔的次数比其他任何方式最坏的次数都少。也就是设计一种最有效的方式来确定临界楼层
*
* @param 39
* layer
* @return the optimal step for the first ball.
*/
public static int getOptimalStep(int N, int[] results, int[] selection) {
if (N == 0) {
results[N] = 0;
return 0;
}
if (results[N - 1] >= 0) {
return results[N - 1];
}
int selectedIndex = 0;
int min = -1;
for (int i = 0; i < N; i++) {
int max = Math.max(i + 1, getOptimalStep(N - i - 1, results, selection) + 1);
if (min < 0) {
min = max;
selectedIndex = i;
} else {
if (min > max) {
selectedIndex = i;
min = max;
}
}
}
selection[N - 1] = selectedIndex;
results[N - 1] = min;
return min;
}
public static void main(String[] args) {
int N = 39;
int[] results = new int[N];
for (int i = 0; i < results.length; ++i) {
results[i] = -1;
}
int[] selection = new int[N];
System.out.println("optimal steps:" + getOptimalStep(N, results, selection));
ArrayList<Integer> steps = new ArrayList<>();
int current = N - 1;
int selectPosition = 0;
while (true) {
if (current < 0) {
break;
}
selectPosition = selectPosition + selection[current];
steps.add(selectPosition);
selectPosition = selectPosition + 1;
current = N - 1 - selectPosition;
}
StringBuilder sb = new StringBuilder();
sb.append("selections:");
sb.append("[");
for (int i = 0; i < steps.size(); ++i) {
if (i != 0) {
sb.append(",");
}
sb.append(steps.get(i) + 1);
}
sb.append("]");
System.out.println(sb.toString());
}
}
这是道动态规划
设函数F(x)为X层大厦时,最坏情况的最小值
比39层大厦,假设我再5楼仍两种结果
1.碎了,那么临界楼就在5楼内,接下来从1楼一层层往上试验。
2.没碎,此时问题相当于在34层大厦中找临界楼即F(34),最后再+1(这是dp的特征:最优子结构)
从5楼仍珠子的最坏情况:max(5,F(34)+1)
那么问题是怎么找最优,其实5楼是我自己随便挑的,我可以把每一楼都试过去
max(1,F(38)+1),max(2,F(37)+1)...max(39,F(0)+1)
恩,我列出了所有方案的最坏情况,接着从他们中找到"最坏的情况扔的次数比其他任何方式最坏的次数都少"
min(max(1,F(38)+1),max(2,F(37)+1)...max(39,F(0)+1))
整理公式(状态转移方程)
F(N)=Min(Max(1,1+F(N-1)),Max(2,1+F(N-2)),……,Max(N-1,1+F(1)))
第一个球测试楼层顺序 8-15-21-26-30-33-35-37-39,碎后第二个球自上一个测试层上一层依次测试
| \ | 详情 |
|---|---|
| 测试楼层 | 0 1 2 3 4 5 6 7 8 |
| 测试次数 | 2 2 3 4 5 6 7 8 8 |
| 测试楼层 | 09 10 11 12 13 14 15 |
| 测试次数 | 03 04 05 06 07 08 08 |
| 测试楼层 | 16 17 18 19 20 21 |
| 测试次数 | 04 05 06 07 08 08 |
| 测试楼层 | 22 23 24 25 26 |
| 测试次数 | 05 06 07 08 08 |
| 测试楼层 | 27 28 29 30 |
| 测试次数 | 06 07 08 08 |
| 测试楼层 | 31 32 33 34 |
| 测试次数 | 07 08 09 09 |
| 测试楼层 | 35 36 37 |
| 测试次数 | 08 09 09 |
| 测试楼层 | 38 39 |
| 测试次数 | 09 09 |
由于是球的损坏性,切考虑的性能为“最坏的情况扔的次数”,所以需要尽可能分成每段最差情况需要测试次数相同,如上分段后,最差情况,33,34,36,37,38,39层,需要测试9次,其余每段的的最差情况为8次。
class Solution{
static int solution(int num){
int[]dp=new int[num+1];
Arrays.fill(dp,Integer.MAX_VALUE);
dp[0]=0;
dp[1]=1;
for(int i=2;i<=num;i++){
for(int j=1;j<=i;j++){
dp[i]=Math.min(Math.max(j,dp[i-j]+1),dp[i]);
}
}
return dp[num];
}
public static void main(String[]args){
System.out.print(solution(39));
}
}
#include <iostream>
using namespace std;
inline int max(int a, int b) { return a > b ? a : b; }
int miniFloorR(int maxFloor)
{
if (maxFloor == 0) return 0;
if (maxFloor == 1) return 1;
int ret = maxFloor;
for (int i = 0; i < maxFloor; i++)
{
int tmp = max(i + 1, miniFloorR(maxFloor - i - 1) + 1);
if (ret > tmp) ret = tmp;
}
return ret;
}
int miniFloorI(int maxFloor)
{
int *arra = new int[maxFloor + 1];
for (int i = 0; i <= maxFloor; i++)
{
arra[i] = i;
for (int j = 1; j <= i; j++)
{
int tmp = max(j, arra[i - j] + 1);
if (tmp < arra[i])
arra[i] = tmp;
}
}
int ret = arra[maxFloor];
delete arra;
return ret;
}
int main()
{
cout << "Result: " << miniFloorI(39) << endl;
return 0;
} package prac_tencent;
public class dp_boli {
static int[][] dp;
static int INF = 0x3f3f3f;
public static void main(String[] args) {
int N = 39;
int M = 2;
dp = new int[N + 1][M + 1];
for (int i = 0; i <= N; i++) {
for (int j = 0; j <= M; j++) {
dp[i][j] = INF;
}
}
int dfs = dfs(N, M);
System.out.println(dfs);
}
public static int dfs(int n, int m) {
if (dp[n][m] != INF) {
return dp[n][m];
}
if (n == 0) {
return dp[n][m] = 0;
}
if (m == 1) {
return dp[n][m] = n;
}
for (int i = 1; i <= n; i++) {
dp[n][m] = Math.min(dp[n][m], 1 + Math.max(dfs(i-1, m-1), dfs(n-i, m)));
}
return dp[n][m];
}
}