关于一道阿里巴巴笔试题的思考

几个礼拜前参加了阿里巴巴的实习生校园招聘,不过在面试关跪了,一起去的同学过的也不多,看来阿里巴巴的要求还是有点高的。之前的笔试难度并不是特别大,主要内容是数据结构、算法等,其中有道题目挺有趣的,想和大家分享一下。题目是这样的:

有4个人在晚上想过一座桥,不过只有一个手电筒,一次只能过2个人,4个人单独过的话分别用时1、2、5、10分钟,2人一起过按慢的时间算,问最短要用多少时间?

不妨分别给人编号为A、B、C、D,看到题目的最直观想法就是让手电筒传递的时间尽量少,所以一种方案为:AB过,A回,AC过,A回,AD过。总耗时为2+1+5+1+10=19分钟。乍一看,答案就应该是这样吧,不过这里还有一种跟好的解法:AB过,A回,CD过,B回,AB过。总耗时为2+1+10+2+2=17分钟,比我们之前一个方案减少了2分钟。到这里,已经无法再举出耗时更少的例子了,看来17分钟时最短的时间。

---------------------------------------------------------------------------------------------------------------------------------

现在人数只有4个,我们可以采用枚举的办法,但是当人数变成40个,400个呢?显然枚举是难以完成的,必须使用一定的方法才能解决这一问题,。POJ1700,就是这么一道题目,人数<1000,单独用时<100,我们现在以过这道题为目标。

仔细想想,想得到最短时间,无非是两种方法(还用刚刚的例子,假设AB已先过桥,T()代表总用时,t()代表单独用时):

1:A回,AC过,T(ABC)=T(AB)+t(A)+t(C)

2:A回,CD过,B回,AB过,T(ABCD)=T(AB)+t(A)+t(D)+t(B)+t(B)

看到这里,隐约感觉到动态规划的气息。假设我们有N个人,把所有人的耗时存入一个有序数组time[ ],而dp[ i]代表过前i个人所耗的最小时间,则有递推公式:

dp[i] = min(dp[i-2]+time[0]+time[i]+time[1]*2 , dp[i-1]+time[0]+time[i])

好了,看来现在我们已经可以着手编程了,以下是用C++实现的代码:


#include<iostream>  
#include<algorithm>
#define min(a,b) (a)<(b)?(a):(b)
using namespace std;
int main()
 {
     int T,n,i,time[1002],dp[1002];
     cin >>T;
     while(T--)
     {
         cin >> n;
         for(i=0;i<n;i++)
            cin >>time[i];
         sort(time,time + n);
         dp[0]=time[0] , dp[1]=time[1];
         for(i=2;i<n;i++)
    dp[i] = min(dp[i-2]+time[0]+time[i]+time[1]*2 , dp[i-1]+time[0]+time[i]);
         cout <<dp[n-1] << endl;        
     }
     return 0;    
}
代码可以Ac,而且非常短小易懂,可以说我们已经解决了这个问题。但是仔细推敲以下,可以发现有个细节可以优化以下,那就是dp数组,我们其实只需要记录三个值就够了,那就是dp[N], dp[N+1], dp[N+2],所以就有了以下优化后的代码:

<pre code_snippet_id="315856" snippet_file_name="blog_20140427_2_8245384" name="code" class="cpp">#include<iostream>  
#include<algorithm>
#define min(a,b) (a)<(b)?(a):(b)
using namespace std;
int main()
{
     int T,n,i,time[1001],dp[3];
     cin >> T;
     while(T--)
     {
         cin >> n;
         for(i=0;i<n;i++)
            cin >>time[i];
         sort(time,time + n);
         dp[0]=time[0],dp[1]=time[1];
         for(i=2;i<n;i++)
    dp[i%3] = min(dp[(i-2)%3]+time[0]+time[i]+time[1]*2 , dp[(i-1)%3]+time[0]+time[i]);	//这是一个滚动数组
         cout <<dp[(n-1)%3] << endl;        
     }
     return 0;    
}


其实dp的方法不止这一种,读者可以开动大脑,应该可以想到其他解法:)。


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值