过桥问题

在一个夜黑风高的晚上,有n(n <= 50)个小朋友在桥的这边,现在他们需要过桥,但是由于桥很窄,每次只允许不大于两人通过,他们只有一个手电筒,所以每次过桥的两个人需要把手电筒带回来,i号小朋友过桥的时间为T[i],两个人过桥的总时间为二者中时间长者。问所有小朋友过桥的总时间最短是多少。
分析:

每次过桥的时候最多两个人,如果桥这边还有人,那么还得回来一个人(送手电筒),也就是说N个人过桥的次数为2*N-3。有一个人需要来回跑,将手电筒送回来(也许不是同一个人,realy?!)这个回来的时间是没办法省去的,并且回来的次数也是确定的,为N-2,如果是我,我会选择让跑的最快的人来干这件事情,但是我错了…如果总是跑得最快的人跑回来的话,那么他在每次别人过桥的时候一定得跟过去,于是就变成就是很简单的问题了,花费的总时间:

T = minPTime * (N-2) + (totalSum-minPTime)

来看一组数据 四个人过桥花费的时间分别为 1 2 5 10,按照上面的公式答案是19,但是实际答案应该是17。

具体步骤是这样的:

第一步:1和2过去,花费时间2,然后1回来(花费时间1);

第二歩:3和4过去,花费时间10,然后2回来(花费时间2);

第三部:1和2过去,花费时间2,总耗时17。

所以之前的贪心想法是不对的。我们先将所有人按花费时间递增进行排序,然后第一次过河的总是耗时最短的两位,假设前i个人过河花费的最少时间为opt[i]。

一.假设前i-1个人已经过河,即河这边还剩1个人,并且手电筒肯定在对岸,所以opt[i] = opt[i-1] + a[1] + a[i] (让花费时间最少的人把手电筒送过来,然后和第i个人一起过河)

二.假设前i-2个人已经过河,则河这边还剩2个人,即第i个和另外一个,并且手电筒肯定在对岸,所以opt[i] = opt[i-2] + a[1] + a[i] + 2*a[2] (让花费时间最少的人把电筒送过来,然后第i个人和另外一个人一起过河,由于花费时间最少的人在这边,所以下一次送手电筒过来的一定是花费次少的,送过来后花费最少的和花费次少的一起过河,解决问题)
所以:

opt[i] = min{opt[i-1] + a[1] + a[i] , opt[i-2] + a[1] + a[i] + 2*a[2] }

递归实现:

time elased1.09829
class Solution {
public:
    int shortesttime(vector<int> vec) {
        if(vec.empty())
            return 0;
        if(vec.size()==1)
            return vec[0];
        sort(vec.begin(),vec.end());
        if(vec.size()==2)
            return vec[1];
        int tmp1=vec[vec.size()-1];
        vec.pop_back();
        int res1=shortesttime(vec);
        vec.pop_back();
        int res2=shortesttime(vec);
        return min(res1+vec[0]+tmp1,res2+vec[0]+tmp1+2*vec[1]);
    }
};
int main()
{

    Solution A;
    vector<int> vec(30);
    int i=1;
    for(auto &v:vec)
    {
        v=i;
        i++;
    }
    boost::timer time;
    int result=A.shortesttime(vec);
    cout<<"time elased"<<time.elapsed()<<endl;
    cout<<result<<endl;
    return 0;
}

自底向上的动态规划:

time elapsed2.1e-05
class Solution {
public:
    vector<int> shortesttime(vector<int> vec) {
        boost::timer time;
        if(vec.empty())
            return {};
        sort(vec.begin(),vec.end());
        vector<int> r(vec.size());
        for(int i=0;i<vec.size();i++)
        {
            if(i<=1)
                r[i]=vec[i];
            else
                r[i]=min(r[i-1]+vec[0]+vec[i],r[i-2]+vec[0]+vec[i]+2*vec[1]);
        }
        cout<<"time elapsed"<<time.elapsed()<<endl;
        return r;
    }
};

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值