题目大意
N个人要过河到对面去,只有一条可容纳两人的船。每个人有自己的驾驶速度,而船要让船上两人中开的慢的那个人来开(有毛病…),即船的行驶速度取决于船上两人驾驶速度低的人的速度。例如船上有AB两人,A速度为100,B速度为10,那么船的行驶速度是10。
问:至少需要多长时间可以将这N个人运送到河对面去。
思考
第一想法:既然船速取决于开的慢的那个人,那么对于最慢的那个人来讲,他过河的时候肯定要自己开过去。进而,次慢的那个人也需要自己开船过去,以此类推,那每一个较慢的人都需要自己开船过去,也就是船开到对面的时候,行驶时间是确定的,所以要使总时间最小,那么回来的时候应该让最快的那个人开船回来。
即,每次让最快的人带最慢的人过河,最快的人回来。再带次慢的人过河,再回来……即,运送两个人过河用时为a[1]×2+a[n]+a[n-1],很快写出代码,果不其然拿到了WA。
以上思路有两处不合逻辑:
①从进而就错了。仔细思考,前边一句话真的可以推出后边那句话吗?次慢的那个人一定要自己开船过去吗?我让最慢的人带次慢的那个人过河不行吗?
②如果按照上述解法,其实是让最快的人带着每一个人过河,根本无所谓先后顺序之分,这怎么符合ACM题的风格?毫无意义的题目。
于是,第二想法:
进一步思考:次慢的那个人让最慢的那个人带过去会怎么样呢?
如果这样的话,相比于之前的方案,直观上的确减少了很多时间。可是问题又来了——谁把船开回去呢?次慢的人吗?脑子里模拟一下,这种方案的时间貌似比上一个方案更差。
如果让最快的人把船开回去呢? 可是最快的人不在岸对面啊!这可咋办呢…………… 你让他提前过来不行吗? 最快的人提前过来也不会耗费很大时间。
即,先让最快的人和次快的人先过来(很明显吧?选择第二快的人和最快的人过来才是最优的方案),让第二(或最)快的人把船开回去,再让最慢的带次慢的过来,再让最(或第二)快的人把船开回去。
马上就想到了,这样运送两个人过河用时为a[1]+2×a[2]+a[n],相比于想法一中的a[1]×2+a[n]+a[n-1],他真的会变得简单吗?
不一定吧?所以想到了,在选择过河方式的时候,需要预先计算出这两种方案所需时间,根据时间短的来方式过河。
总结
从最慢的人开始运送:
①如果剩余1人,那么时间就是a[1];
②如果剩余2人,那么时间就是a[2];
③如果剩余3人,那么时间就是a[1]+a[2]+a[3];
④如果剩余人数大于等于4人,那么以运送两人过河为节点进行计算,有两种方案:
一是用最快的人来协助运送每一个人,时间为a[1]×2+a[n]+a[n-1];
二是让最快的人和第二快的人作为媒介运送船只,最慢的人与第二慢的人一起过河,时间为:a[1]+2×a[2]+a[n];
这两个时间做对比,采用用时更少的方案。
核心代码:
while(n>=4){
sum=minx(sum+a[1]+2*a[2]+a[n],sum+2*a[1]+a[n-1]+a[n]);
n-=2;
}
if(n==3) sum+=a[1]+a[2]+a[3];
if(n==2) sum+=a[2];
if(n==1) sum+=a[1];
AC代码:
#include<cstdio>
#include<math.h>
using namespace std;
int minx(int a,int b){return a>b ? b:a;}
int main()
{
int t,sum;
int a[1005];
scanf("%d",&t);
while(t--){
int n;
sum=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
while(n>=4){
sum=minx(sum+a[1]+2*a[2]+a[n],sum+2*a[1]+a[n-1]+a[n]);
n-=2;
}
if(n==3) sum+=a[1]+a[2]+a[3];
if(n==2) sum+=a[2];
if(n==1) sum+=a[1];
printf("%d\n",sum);
}
return 0;
}