nyoj47-过河问题(贪心算法)--算法笔记

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44494648/article/details/93212300

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;
}

猜你喜欢

转载自blog.csdn.net/weixin_44494648/article/details/93212300