微软过桥问题
微软的过桥问题:4个人在晚上过一座小桥,过桥时必须要用到手电筒,只有一枚手电筒,每次最多只可以有两人通过, 4个人的过桥速度分别为1分钟、2分钟、5分钟、10分钟,试问最少需要多长时间4人才可以全部通过小桥?
思路:刚一看到这道题,思路比较狭窄,就拿着笔把人移过来再移过去,第一次做出的答案是19(错误答案).后来上网上去找参考答案,发现是17.网上的答案只给出得到17分钟的解决方式,并没有说出到底应该怎么样解这个答案才能百分百确定就是最少的长时?因此,开始乱想:穷举法,贪心法,参考《牧师与野人过河问题》,DFS,图论找最小路径法,乱蒙。最开始参照<牧师与野人>的算法,发现,无果。再用穷举法,发现转移状态实在太多,没有办法递归下去,然后用贪心选择,发现最后倒在vector.erase上面了,而且递归的时候没有办法恢复现场,因为需要在vector的中间删除一个元素,然后再恢复,并不像《字符串全排列》那样可以push_back,再pop_back就能恢复现场。最后想,既然无法恢复现场,那么不妨可以从一开始就不要破坏原来现场,而只是拷贝原来vector的一份拷贝,只对拷贝进行操作,那么递归返回时就没有必要恢复原来现场,当然原来现场也并没被破坏,这样一来,每次递归结束之后,我们可以得到一个过桥方案,递归堆栈中的内容被清除(当然对原来内容并没有任何影响,下一次可以继续正常递归而不需要担心原来内容被改变而带来的不确定后果)。其于这个方案可以得到下面的程序(贪心法)。
#include<vector>
#include<iostream>
using namespace std;
void removeKeyFromVec(vector<int>& veci,int key)
{
for(vector<int>::iterator iter=veci.begin(); iter!=veci.end(); ++iter)
{
if( *iter == key)
{
iter = veci.erase(iter);//erase的使用要小心
return ;
}
}
}
void CrossBridge(vector<int>& src, vector<int>& des, int size, int TimeSum)
{
//程序出口,当只有2个人时,直接过桥
if(size == 2)
{
cout<<"A->B:"<< src[0] << " AND " << src[1]<<endl;
cout<<"Time : "<<TimeSum + max(src[0], src[1]) <<endl;
cout<<endl;
return ;
}
//从size大小的数列中找出两个数的组合,例1,2,5有(1,2),(1,5),(2,5) 3种组合
for(int i=0; i<size; i++)
{
for(int j=i+1; j<size; j++)
{
cout<<"i="<<i<<" j="<<j<<endl;
//只操作src的拷贝,否则递归之后需要还原现场,较麻烦
vector<int> srcTemp(src) ;
vector<int> desTemp(des) ;
int goTime1 = srcTemp[i] ;
int goTime2 = srcTemp[j] ;
//2人过桥
desTemp.push_back(goTime1);
desTemp.push_back(goTime2) ;
cout<<"A->B: " << goTime1 <<" AND "<< goTime2 <<endl;
removeKeyFromVec( srcTemp ,goTime1);
removeKeyFromVec( srcTemp ,goTime2);
int minBackTime = desTemp[0];
for(int k=0 ; k<desTemp.size(); k++)
{
if( desTemp[k] < minBackTime)
{
minBackTime = desTemp[k];
}
}
//1人返回