nyoj47-过河问题
题目描述:
在漆黑的夜里,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
分析:
前提
- 一定要有人来回跑动送手电筒,这个人跑的越快越好。
- 最慢的人过河以后,不能让他们再回来送手电筒。
结论
- 每次送走最慢的,不会对之后的情况产生影响,这是一个贪心问题
具体情况(假如只有四个人)
-
假设时间为 1,2,5,8,记作 t1,t2, t3,t4;
- 两人过桥后,需要把手电筒送回,最容易想到的是让最快的人担任来回送电筒。因此,第一种办法:先让甲乙过去(2分钟),甲回来(1分钟),甲丙过去(5分钟),甲回来(1分钟),甲丁再过去(8分钟),总共需要17分钟就可以让四个人都过去。而正确答案是第二种办法:先让甲乙过去(2分钟),甲回来(1分钟),丙丁过去(8分钟),乙回来(2分钟),甲乙再过去(2分钟),总共需要15分钟就可以让四个人都过去。
- 这种方法的关键点,让两个最慢的人同时过桥。 最优时间就是:T1=t2+t1+t4+t2+t2
-
当时间是1、4、5、8时
- 第一种方法:先甲乙过去(4分钟),甲回来(1分钟),甲丙过去(5分钟),甲回来(1分钟),甲丁再过去(8分钟),总共需要19分钟就可以让四个人都过去。第二种方法:先让甲乙过去(4分钟),甲回来(1分钟),丙丁过去(8分钟),乙回来(4分钟),甲乙再过去(4分钟),总共需要21分钟就可以让四个人都过去。
- **这一次,两个最慢的人一起过去反而更慢了。**最优时间:T2=t2+t1+t3+t1+t4;
-
所以,四个人过河的方式,和时间有关
- 两种方法的差异在于是否要让第二快的也送一次灯,其实只要比较T1和T2的大小二者之差为:(T1+T3)-2T2。
结论:如果(T1+T3)大于2T2,第二种方法优;如果(T1+T3)小于2T2,第一种方法优;如果(T1+T3)等于2T2,两种方法无差异。
推广
根据之前的分析,我们可以确定,每次如果只是送最慢的过河,之后灯要再回到对岸,则送最慢的人过河之后,剩余的人过河时间不会被之前过河的人影响,所以,只要每次送过河的人时间最优,则总时间最短。
由上一个例子可以看出来,可以由t1去送tn,t1回来,t1,送t(n-1),t1回来,这是,这相当于第一种方法,即一号送二号,再回来送三号,在回来,送四号。这个是一个逐一由最快的人送灯的方法。
但四个人人的时候,还有一个方法,就是用t1与t2送最慢的两个人过河,就是第二种方法:一号和二号先过河,一号带灯回来三四号拿灯过河,二号带等回来,至此,一号二号送三号四号过河,一号二号又回到对岸,相当于第一中方法中一号送了两次人(相送四号,再送三号),剩余的n-2个人,又是一个新问题,继续这样求解,只要选出这两个人过河最快的方法就行。
方法一:tn+t1+tn-1+t1
方法二:t2+t1+tn+t2
选出最快的方法
#include<stdio.h>
#include<stdlib.h>
int an[1005];
int gh(int an[],int n,int time);
int main()
{
int kkk;//k组数
scanf("%d",&kkk);
while(kkk--)
{
int n;
scanf("%d",&n);
int i,j,k;
for(i=0;i<n;i++)
{
scanf("%d",&an[i]);
}
int time=0;
//冒泡排序
for(i=0;i<n-1;i++)
{
for(j=0;j<n-1-i;j++)
{
if(an[j]>an[j+1])
{
k=an[j];
an[j]=an[j+1];
an[j+1]=k;
}
}
}
while(n>=4)//循环
{
//选择方法一或二
if(an[0]+an[n-2]<2*an[1])
{
time+=an[0]+an[n-2]+an[n-1]+an[0];//方法一
}
else
{
time+=an[n-1]+an[1]*2+an[0];//方法二
}
n-=2;//人数减二
}
if(n<=3)
{
if(n==1)//直接过河
{
time+=an[0];
}
if(n==2)//一起过河
{
time+=an[1];
}
if(n==3)//一二先过河,一回来,和三过河
{
time+=an[0]+an[1]+an[2];
}
}
printf("%d\n",time);
}
return 0;
}