学习贪心算法所做的第一题,话不多说,题如下:
“贪心”粗浅理解:每一步都是最优解。
单刀直入,开始分析:
既然是讨论最短时间,那就先把时间分解出来看看有没有突破点
-->时间 = 过桥时间 + 手电筒传递时间
时间详解:
“手电传递”:手电只有一个,只要人数大于两人就必定要有传递时间,这个似乎没有什么方式能减缓,用最快的人来回跑。
“过桥时间”,既然明确指出过桥时间是最慢那人的时间,于是慢的人就一定会吃掉这个固定的时间,他的同伴再怎么快也没用,我管他与他同伴的时间差叫做“损耗”,“损耗”产生,那就尽量减小嘛,过桥时间相差不大的两人一起过桥是我想到的一个方法。
策略:
1:将最快的两人送过去;
2:最快的一个把手电筒送回来;
3:最慢的两个过去;
4:第二快的人回来把第一快的人带走。
最慢最快的人如何锁定?简单,排个序,这里我用了升序-->
数组a[N]是所有人的过河时间,N为总人数。
sum_time是策略需要的总时间。(这个是代码切片,完整代码在下面)
解释:“N-1>2”这里用的是地址下标,下标0和1作为最快的两人早就固定在右边了,作为特殊情况拿出来的是左边到最后只剩下一个人的时候(毕竟每次都是少两人嘛)。
其实这也不是个完美的法子,毕竟也不可能存在真正完美的方法嘛,
这个策略其实是用最快的人回跑的时间和通过减少“损耗”赚取的时间相抵消,但如果所有人的过桥时间都很差别不大呢,来回跑的时候是最快的两人单独跑,这又何尝不是又一种“损耗”呢?
(例:1 2 3 3 3 3 3 3 3 3 78 89 99)
这种类似的情况可不罕见。
策略2:(或者叫优化)
有点简陋,其实就让最快的人来回接送其他人
毕竟“手电传递”的时间无法改变,这个任务由最快的人来完成,那么在无法偷奸耍滑的情况下,似乎也只能用这直接的方式了
就把它当成一个扩展插进去吧(它所解决的问题是策略1在面对“那一堆3”时疲乏的情况)
到此为止了,下面是完整代码:
#include <iostream>
#include <algorithm>
using namespace std;
int main(void)
{
int N, sum_time = 0; // 总人数与总时长
cout << "需要过河的人数:";
cin >> N;
int *a = new int[N];
cout << "每个人所需的过河时间:";
for (int i = 0; i < N; i++)
cin >> a[i];
auto f = [](int x, int y)
{
return x < y;
}; // 排序准则
sort(a, a + N, f); // 升序
for (sum_time = a[1]; N - 1 > 2; N -= 2)
{ // 先将最快的两人送过去,且每次送完手电筒两人都会在右边待命,所以0号和1号其实是不在左边的
int time1, time2;
time1 = a[0] + a[N - 1] + a[1] * 2;
/*这是策略1
由于过桥必须要有手电筒,这里的策略是:
最快的两人先过去->
最快的人携手电筒回来->最慢的两人过去->第二快的人携手电筒回来并和最快的人回去
至此完整的送去了最慢两个人
*/
time2 = a[0] + a[N - 1] + a[0] + a[N - 2];
/*策略扩展:
单纯是是由最快的人来回接送慢的人(为配合策略一,所以按两个轮次一算
*/
sum_time += min(time1, time2);
}
if (N - 1 == 2) // 最快的两人一直在右边,所以0号与1号是不存在的
sum_time += a[0] + a[2]; // 最快的回来接走最后一人
cout << "耗时:" << sum_time << endl;
delete[] a;
}
这个是后日编辑,并不是当时写完就上传了。
小白的第一篇,话有些多,如若不满,还请........................尽情挥洒您的口水。