NYOJ 47-过河问题(贪心)

过河问题

时间限制: 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


//这道题刚开始有点难想到
//起初我以为,2个最小的(标记为min1和min2)先过去,然后让最小的(min1)那个带灯回来,然后再让最大的2个人(max1,max2)过去
//然后再让min2回来,再带min1过去,然后min1回来,然后继续让还没过桥的最大的2个过去,依次循环下去
//样例 1  2  5  10
//(1,2)过去(耗时2),1回来(耗时1),(5,10)过去(耗时10),2回来(耗时2),(1,2)过去(耗时2),共2+1+10+2+2 = 17
//然后...提交,wa

//下面再看一组数据 1  4  5  8
//如果按照上面的做法,最终结果会是4+1+8+4+4 = 21  (视为第一种方法)
//然而可以这样,(1,4)过去(耗时4),1回来(耗时1),(1,5)过去(耗时5),1回来(耗时1),(1,8)过去(耗时8),共4+1+5+1+8 = 19(第二种)
//通过这两组数据可以发现,需要判断要不要让min2回来
//如果让min2回来,那么让2个最大的过去的时间会是max1+min1+2*min2;
//不让min2回来,那么让2个最大的过去的时间会是max1+max2+2*min1;
//所以需要判断max1+min1+2*min2和max1+max2+2*min1的大小,即2*min2和min1+max2的大小
//if(2*min2>=min1+max2)则采取第二种,否则采取第一种
#include <stdio.h>
#include <queue>
using namespace std;

priority_queue<int, vector<int>, greater<int> >q1;        //存还没过桥的人,时间是小到大
priority_queue<int, vector<int>, less<int> >q2;           //存还没过桥的人,时间是大到小
priority_queue<int, vector<int>, greater<int> >q3;        //存已经过桥的人,时间是小到大
int main()
{
	int T;
	scanf("%d", &T);
	while(T--)
	{
		while(!q1.empty())
			q1.pop();
		while(!q2.empty())
			q2.pop();
		while(!q3.empty())
			q3.pop();
		int n;
		scanf("%d", &n);
		for(int i=0; i<n; i++)
		{
			int num;
			scanf("%d", &num);
			q1.push(num);
			q2.push(num);
		}
		int sum = 0;      //总耗时
		int min1, min2, max1, max2, k=0;
		//先让两个耗时最少的人过去
		min1 = q1.top();
		q1.pop();
		if(n==1)
		{
			printf("%d\n", min1);
			continue;
		}
		min2 = q1.top();
		q1.pop();
		q3.push(min1);
		q3.push(min2);
		sum += min2;

		while(q3.size()<n)
		{
			//只剩一个人,让min1带他过桥
			if(q3.size()==n-1)
				{
					sum +=	q2.top() + min1;
					break;
				}
			max1 = q2.top();
			q2.pop();
			max2 = q2.top();
			q2.pop();
			if(2*min2>=min1+max2)
			{
				//让min1将max1和max2依次带过去
				sum += max1 + max2 + 2*min1;
				q3.push(max1);
				q3.push(max2);
			}
			else
			{
				//min1回来,让max1和max2过去,min2回来,带min1一起过去
				sum += max1 + 2*min2 + min1;
				q3.push(max1);
				q3.push(max2);
			}
		}
		printf("%d\n", sum);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值