贪心——过河问题
我是题目链接
47-过河问题
内存限制:64MB 时间限制:1000ms 特判: No
难度: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
解题思路:
当n=1或者n=2时:所有人直接过河即可。当n=3时的一个人一起过去。当n=4时:假设time[0]为用时最短的人所用的时间,time[1]为用时第二短的人所用的时间, time[n-1]为用时最长的人所用的时间,
time[n-2]为用时第二长的人所用的时间。则有两种过河方式:
2time[0]+time[n-1]+time[n-2]表示:用时最长的人和用时最短的人先一起过去,然后用时最短的人把手电筒带回来,再和用时第二长的人一起过去,用时最短的人回来。
2time[1]+time[0]+time[n-1]表示:用时最短和用时第二短的人一起过去,然后用时最短的人把手电筒带回来,然后用时最长和用时第二长的人一起过去,用时第二短的人回来。
比较两种过河方式:2time[0]+time[n-1]+time[n-2]<2time[1]+time[0]+time[n-1]
#include < cstdio>
#include< algorithm>
using namespace std;
int mintime(int n,int * a)
{
sort(a,a+n);
int res=0;
–n;
while(n>2)
{
res+=min(a[0]+a[1]*2+a[n],a[0]*2+a[n]+a[n-1]);分析:每一轮让最快的两个人先过桥,最快的一个人拿手电回来,然后这边最慢的两人先过去,桥那边最快的一个人回来类推
补充:其实还没有考虑完全,比如 下面一个例子:1,5, 7, 12
按上面的方面,当7, 12到对面共花了23
但是如果1带7过去,1 回带12过去,1回,确只用了21
n-=2;
}
if(n=0)
//当n=1或者n=2时:所有人直接过河即可。当n=3时:用时最长和用时最短的人先一起过去,然后用时最短的人回来,再在和剩下的一个人一起过去。当n=4时:假设time[0]为用时最短的人所用的时间,time[1]为用时第二短的人所用的时间, time[n-1]为用时最长的人所用的时间,
res+=a[0];
else if(n==1)
res+=a[1];
else
res+=a[0]+a[1]+a[2];
return res;
}
这样相当于每次都过去了两个人,所以n-=2,
再对剩下的n-2个人执行相同的操作,直至不足4人即可。
贪心策略
问题分析:
我们对用时进行排序。
首先根据他给的示例,看如何才能得到最小的用时17。1、2→2,表示1和2同时过桥,用时为2:
1、2→2
1←1
5、10→10
2←2
1、2→2
综上2+1+10+2+2=17
从中我们可以看出首先令用时最小的两个人过桥,然后用时最小的回来,其次让用时最多的两个人过桥,然后让次小的人回来,从中我们可以整理出一个循环:
最小、次小→次小
最小←最小
最大、次大→最大
次小←次小
通过上面这个循环我们不断让用时最大和次大的人过桥。
通过以上分析我们编写了代码,根据我的个人测试的几组数据都是成功的。但是,当提交代码时结果是错误的。
经过一系列的分析,我找到了一个以上循环的错误特例,如2、4、5、7,如果根据上面的循环,过桥顺序如下:
2、4→4
2←2
5、7→7
4←4
2、4→4
过桥用时为4+2+7+4+4=21.但是存在更短的过桥时间如下:
2、7→7
2←2
2、5→5
2←2
2、4→4
过桥用时为7+2+5+2+4=20.
二者的区别是什么呢?
前者在运送最高和次高用时的人采取的是先让两个最小用时的人过桥来回策应。而后者的策略是让最小用时的人单独护送最高用时的两个人过桥。后者的过桥循环如下:
最小、最大→最大
最小←最小
最小、次大→次大
最小←最小
所以在运送最大和次大用时的人过桥时需要作出判断,是采用策略1还是采用策略2。我们来计算一下策略1和策略2的用时,选择用时较小的来运送最大和次大用时的人过桥。
分析:每一轮让最快的两个人先过桥,最快的一个人拿手电回来,然后这边最慢的两人先过去,桥那边最快的一个人回来类推
最后附上AC代码:
#include<stdio.h>
#include<algorithm>
using namespace std;
int fun(int n,int *a)
{
sort(a,a+n);
int sum=0;
n--;
while(n>2)
{
sum+=min(a[0]+a[1]*2+a[n],a[0]*2+a[n]+a[n-1]);
n-=2;
}
if(n==0)
sum+=a[0];
else if(n==1)
sum+=a[1];
else
sum+=a[0]+a[1]+a[2];
return sum;
}
int main()
{
int i,t,n,a[1005];
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d",&a[i]);
printf("%d\n",fun(n,a));
}
return 0;
}