两则面试题(动态规划)

某幢大楼有100层。你手里有两颗一模一样的玻璃珠。当你拿着玻璃珠在某一层往下扔的时候,一定会有两个结果,玻璃珠碎了或者没碎。这幢大楼有个临界楼层。低于它的楼层,往下扔玻璃珠,玻璃珠不会碎,等于或高于它的楼层,扔下玻璃珠,玻璃珠一定会碎。玻璃珠碎了就不能再扔。现在让你设计一种方式,使得在该方式下,最坏的情况扔的次数比其他任何方式最坏的次数都少。也就是设计一种最有效的方式。

例如:有这样一种方式,第一次选择在60层扔,若碎了,说明临界点在60层及以下楼层,这时只有一颗珠子,剩下的只能是从第一层,一层一层往上实验,最坏的情况,要实验59次,加上之前的第一次,一共60次。若没碎,则只要从61层往上试即可,最多只要试40次,加上之前一共需41次。两种情况取最多的那种。故这种方式最坏的情况要试60次。仔细分析一下。如果不碎,我还有两颗珠子,第二颗珠子会从N+1层开始试吗?很显然不会,此时大楼还剩100-N层,问题就转化为100-N的问题了。

那该如何设计方式呢?

根据题意很容易写出状态转移方程:N层楼如果从n层投下玻璃珠,最坏的尝试次数是:clip_image002[6]

那么所有层投下的最坏尝试次数的最小值即为问题的解:clip_image002[8]。其中F(1)=1.

/*
 *侯凯,2014-9-15
 *功能:100楼层抛珠问题
 */
#include<iostream>
using namespace std;

int dp[101];
//N<=100;
int floorThr(int N)
{
    for(int i=2;i<=N;i++)
    {
        dp[i]=i;
        for(int j=1;j<i;j++)
        {
            int tmp = max(j,1+dp[i-j]);
            if(tmp<dp[i])
                dp[i] = tmp;
        }
    }
    return dp[N];
}

int main()
{
    dp[0]=0;
    dp[1]=1;
    int dis = floorThr(100);
    cout<<dis<<endl;
    system("Pause");
}

输出为14,说明在合适的楼层抛玻璃珠,最差情况下只需14次可找到临界层。

答案是先从14楼开始抛第一次;如果没碎,再从27楼抛第二次;如果还没碎,再从39楼抛第三次;如果还没碎,再从50楼抛第四次;如此,每次间隔的楼层少一层。这样,任何一次抛棋子碎时,都能确保最多抛14次可以找出临界楼层。

求最大子段积

问题定义:对于给定序列a1,a2,a3……an,寻找它的某个连续子段,使得其和最大。如( 1,2,3,4,0,5,4,-3,-2 )最大子段是{ 11,-4,13 }其和为20。既然是连续子串,当前的最大值只可能由前一位置的最大值(最小值)与当前值相乘得到,状态转移方程为:

clip_image002[10]

/*
 *侯凯,2014-9-15
 *功能:最大子串积
 */
#include<iostream>
using namespace std;

int bigestmult(int *a,int n)
{
    int *maxarrary = new int[n];
    int *minarrary = new int[n];
    maxarrary[0]=a[0];
    minarrary[0]=a[0];
    int res = a[0];
    for(int i=1;i<n;i++)
    {
        maxarrary[i] = max(max(maxarrary[i-1]*a[i],minarrary[i-1]*a[i]),a[i]);
        minarrary[i] = min(min(maxarrary[i-1]*a[i],minarrary[i-1]*a[i]),a[i]);
        if(maxarrary[i]>res)
            res = maxarrary[i];
    }
    delete [] maxarrary;
    delete [] minarrary;
    return res;
}

int main()
{
    int a[]={1,2,3,4,0,5,4,-3,-2};
    cout<<bigestmult(a,9)<<endl;
    system("Pause");
}

还有很多类似的问题,可参见:http://blog.csdn.net/liufeng_king/article/details/8490770,整理地非常完整。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值