贪心过河问题

本文探讨了N位旅行者如何使用唯一的手电筒通过狭窄桥梁的问题,提出了两种高效的算法解决方案,并通过C语言实现了算法流程。
过河问题
时间限制:1000 ms  |  内存限制:65535 KB
难度:5

描述

    在漆黑的夜里,N位旅行者来到了一座狭窄而且没有护栏的桥边。如果不借助手电筒的话,大家是无论如何也不敢过桥去的。不幸的是,N个人一共只带了一只手电筒,
而桥窄得只够让两个人同时过。如果各自单独过桥的话,N人所需要的时间已知;而如果两人同时过桥,所需要的时间就是走得比较慢的那个人单独行动时所需的时间。
问题是,如何设计一个方案,让这N人尽快过桥。 

输入
    第一行是一个整数T(1<=T<=20)表示测试数据的组数
    每组测试数据的第一行是一个整数N(1<=N<=1000)表示共有N个人要过河
    每组测试数据的第二行是N个整数Si,表示此人过河所需要花时间。(0<Si<=100)
输出
    输出所有人都过河需要用的最少时间
样例输入

    1
    4
    1 2 5 10

样例输出

    17


----------
对于时间排序得到S(1) <= S(2) .... S(n-1) <= S(n)
/*贪心递归式:
首先由题目已知,无论单独过桥,还是两人一起过桥均需要手电筒,显然,当剩余未过桥的人数N > 1 ,是必定要两个一起过桥的,但过桥后,仍需要一个人送回手电筒,
显然送手电筒的人必须用时较短,因此我们选择S(1)送手电筒,而两个人一起过桥,显然为了用时最短,我们需要将剩余未过桥N人中用时最长的两个人S(N),S(N- 1)
先过桥,然后让S(1)送手电筒,但现在问题在于,由于只有一个手电筒,所以,让P,Q先过桥显然是不合理的,因为过桥后仍需送回手电筒,这就要求在P,Q过桥之前,
S(1)到达桥对面。
但我们再深入思考一下,若只让S(1)一人先过桥,然后再送回手电筒,显然也是不合理的,因为S(N - 1),S(N)过桥后,无人留在桥对面送回手电筒(我们尽量不让
时间较长的S(N - 1)和S(N - 1)送回手电筒),所以需要S(1)和S(2)一起过桥,然后S(1)送回手电筒,让S(N - 1)和S(N)过桥后,再由S(2)送回。以此类推。
这时,第一种方案出来了,即:sum1 = S(1) + 2 * S(2)+ S(N);
但我们再仔细分析可以发现,在这个方案中,S(1)必定存在,但由于具有2 * S(2),若S(2)相对较大时,(这是可能的,因为当剩余的人越来越少时,过桥时间越来越
接近)此时,第二种方案可能比第一种方案所需时间较短,即 sum2 = 2 * S(1) + S(N - 1) + S(N);此时,S(1)和S(N),S(N  - 1)一起过桥,且返回2次,我们可
以用sum2 - sum1 = S(1) + S(N - 1) - 2 * S(2);当S(1) + S(N - 1) < 2 * S(2)时方案二较优。
因此我们可得方程式:
 SUM = SUM + MIN(S(n - 1) + 2 * S(1) + S(n),S(n) + S(1) + 2 * S(2))
*/

----------


#include <stdio.h>
#include <stdlib.h>
#define MIN(a,b) ((a) > (b) ? (b) :(a))
int cmp(const void *a, const void *b)
{
	return *(int *)a - *(int *)b;
}
int main()
{
	int T, N;
	int s[1001];
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d", &N);
		int i = 1, n = N;
		while (N--)
		{
			scanf("%d", &s[i]);
			++i;
		}
		qsort(s, i, sizeof(s[1]), cmp);
		int sum = 0;
		while (n > 3)
		{
			sum = sum + MIN(s[n - 1] + 2 * s[1] + s[n], s[n] + s[1] + 2 * s[2]);
			n = n - 2;
		}
		if (n == 3)
			sum = sum + s[1] + s[2] + s[3];
		else if (n == 2)
			sum = sum + s[2];
		else
			sum = sum + s[1];
		printf("%d\n", sum);
	}
	return 0;
}

<think>好的,我现在需要解决用户提出的关于如何用贪心算法解决小船过河问题。首先,我得回顾一下用户提供的引用内容,确保理解问题的具体要求。根据引用[1]和[2],问题描述是N个人过河,船每次只能载两个人,过河时间由较慢的人决定,船返回需要一人划回,要求找到最快的总时间。而引用[3]提到另一个类似的问题,但涉及体重和船的最大载重,求最少船只数。不过用户的问题明显是关于时间优化的小船过河,所以重点在引用1和2。 首先,我需要明确问题的核心。每次过河需要两个人,回来需要一个人,因此需要多次往返。目标是让总时间最短。根据引用[2],解决方案是贪心算法,先将时间排序,然后采用两种策略选择最优的方式送最慢的两个人过河。这时候,我需要理清楚这两种策略的具体情况和如何选择。 具体来说,当处理剩下的最慢两个人时,有两种可能的方式。第一种是让最快和次快的人先过去,最快回来,然后最慢和次慢过去,次快回来。这样时间计算为t[0] + 2*t[1] + t[n-1]。另一种方式可能是让最快的人带最慢的人过去,然后最快回来,再带次慢的人过去,再回来。这种情况下时间是2*t[0] + t[n-1] + t[n-2]。这时候需要比较这两种情况的时间,选择较小的那个。这应该是贪心策略的关键点。 接下来,我需要考虑如何将这些步骤转化为算法步骤。首先排序数组,然后循环处理最慢的两个人,直到所有人都过河。在每一步中,比较两种策略的时间,选择较小的累加到总时间中。如果最后剩下一个人或两个人,直接加上他们的时间。 然后,我需要验证这个算法的正确性。例如,当人数较多时,每次处理两个最慢的人,能确保总体时间最优。这可能需要数学归纳法或反证法来证明贪心选择的有效性。同时,需要考虑边界情况,比如当n=1、2、3时的情况,确保算法覆盖这些情况。 另外,用户提供的引用中有一个Python代码示例,但那是快速排序的例子。我需要为小船过河问题编写相应的代码。代码的大体结构应该是这样的:排序时间数组,初始化总时间,然后循环处理最大的两个元素,根据策略选择时间,直到处理完所有人。最后处理剩下的1或2人。 还需要注意,当n为奇数时,最后可能剩下三个人,这时候可能需要特殊处理。或者,在循环中处理到只剩三个人的时候,直接应用另一种策略。例如,当剩下三人时,最快的带最慢的过去,回来,再带次慢的过去,总时间为t[0] + t[1] + t[2]。或者可能需要分情况讨论。 此外,引用[3]的问题虽然不同,但可能给用户带来混淆。需要明确用户的问题是关于时间的还是船只数的,但根据用户的问题描述,是前者,所以可以暂时忽略引用3的内容,除非用户有后续问题。 现在,我需要将这些思路整理成一个清晰的步骤说明,并编写相应的Python代码。同时,生成相关问题,帮助用户深入理解问题,比如关于算法的时间复杂度、正确性证明、不同情况的处理等。</think>### 贪心算法解决小船过河问题 **问题分析** 小船过河问题的核心在于通过贪心策略选择最优的两人组合,使得总过河时间最短。每次过河后必须有一人返回,因此需要合理安排往返顺序,减少时间浪费[^1][^2]。 --- ### 分步解决方案 1. **排序时间** 将所有人过河所需时间按升序排序,得到数组 $t = [t_0, t_1, ..., t_{n-1}]$,其中 $t_0$ 最快,$t_{n-1}$ 最慢。 2. **贪心策略选择** - **策略1**:用最快和次快的人运送最慢的两人 总时间为 $t_0 + 2t_1 + t_{n-1}$ - **策略2**:用最快的人单独运送最慢的两人 总时间为 $2t_0 + t_{n-1} + t_{n-2}$ - **选择更优策略**:比较两种策略的时间,取较小值累加到总时间中。 3. **递归/循环处理剩余人员** 重复步骤2,每次处理剩下的最慢两人,直到剩余人数 ≤ 2。 4. **处理剩余人员** - 若剩余1人:时间加 $t_0$ - 若剩余2人:时间加 $t_1$ --- ### Python代码实现 ```python def min_time_cross_river(times): times.sort() total_time = 0 n = len(times) while n > 3: # 比较两种策略的时间,选择更优的 strategy1 = times[0] + 2*times[1] + times[-1] strategy2 = 2*times[0] + times[-1] + times[-2] total_time += min(strategy1, strategy2) n -= 2 # 处理完最慢的两人 times = times[:-2] # 移除已过河的两人 # 处理剩余人员 if n == 3: total_time += times[0] + times[1] + times[2] elif n == 2: total_time += times[1] elif n == 1: total_time += times[0] return total_time # 示例 times = [1, 2, 5, 10] print(min_time_cross_river(times)) # 输出:17 ``` --- ### 正确性证明 贪心策略的关键在于:**每次运送最慢两人时,选择局部最优解**。通过数学归纳法可以证明,局部最优解的累加最终得到全局最优解。 --- ### 时间复杂度 排序时间 $O(n \log n)$,循环处理时间 $O(n)$,总体时间复杂度为 $O(n \log n)$。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值