POJ1700Crossing River(贪心+类DP)

探讨了在有限条件下,如何通过合理安排人员过河顺序,以最短时间完成全员渡河任务的算法问题。分析了不同策略的优劣,并给出了一种有效的解决思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值