信息学奥赛一本通 1232:Crossing River | OpenJudge NOI 4.6 702:Crossing River

【题目链接】

ybt 1232:Crossing River
OpenJudge NOI 4.6 702:Crossing River
一本通里的翻译不够完整,OpenJudge中的英文原题中有对数据大小的限制:
样例组数 1 ≤ T ≤ 20 1\le T \le 20 1T20,人数 n ≤ 1000 n \le 1000 n1000,每个人过河时间 s ≤ 100 s \le 100 s100

【题目翻译】

描述

N个人想要渡河,只有一条最多能载两人的船。因此必须安排某种运送方案,让船在两岸间划来划去以使所有的人都能渡河。每人都有一个不同的划船速度,两人一同划船的速度取决于划船更慢的那个人。你的任务是确定一种策略,可以使这些人渡河的时间最少。

输入

输入的第1行包含一个单独的整数T(1<=T<=20),是测试数据的组数。然后是T组数据。每组数据的第一行是N,第二行包含N个整数,给出了每个人渡河的时间。总人数不多于1000人,没有人的渡河时间超过100秒。

输出

对于每组测试数据,打印一行,为N个人渡河所需的总秒数。

样例输入

1
4
1 2 5 10

样例输出

17

来源

POJ Monthly–2004.07.18

【题目考点】

1. 贪心

小船过河问题
同样问题可以参考:CSPJ2021初赛 第15题

【解题思路】

1. 思路

经典小船过河问题
考虑每次将2个人渡到对岸,有两种方案:
假设参与渡河的4人a、b、c、d的渡河时间分别为: t a , t b , t c , t d t_a,t_b,t_c,t_d ta,tb,tc,td且有 t a ≤ t b ≤ t c ≤ t d t_a\le t_b \le t_c \le t_d tatbtctd,要将c与d渡到对岸。
方案1:a作为船夫
渡河流程为:a与c到对岸,a回来,a与d到对岸,a回来。
渡河时间为渡河的二人中渡河时间较长的时间,所以该方案的渡河时间为: 2 t a + t c + t d 2t_a+t_c+t_d 2ta+tc+td
显然在该方案下, t a t_a ta越小渡河时间越短。所以a应该为渡河时间最短的人。
方案2:a与b作为船夫
渡河流程为:a与b到对岸,a回来,c与d到对岸,b回来。
该方案的渡河时间为: 2 t b + t a + t d 2t_b+t_a+t_d 2tb+ta+td
显然在该方案下, t a t_a ta t b t_b tb应该尽可能小,而 t c t_c tc应该尽量接近 t d t_d td

其他将两个人渡到对岸的方案,都不如上述两种方案。这里不再做详细论证。
而上述两种方案的优劣是不固定的,所以需要每次判断哪种方案的渡河时间更短。

2. 具体做法

将渡河时间升序排序,选择渡河时间最短的两个人作为船夫a与b,每次选择渡河时间最长的两个人作为c与d进行渡河。比较两种渡河方案,看哪种方案渡河时间更短,选择渡河时间更短的渡河方案。
重复上述过程,每次渡过去两个人,直到剩余人数小于4。
如果剩下3人,按渡河时间从小到大分别为a,b,c,那么最佳的渡河方案为:a与c到对岸,a回来,a与b到对岸,渡河时间为: t a + t b + t c t_a+t_b+t_c ta+tb+tc
如果剩下2人,则a,b两人可以直接渡到对岸,渡河时间为 t b t_b tb
如果剩下1人(【注意】如果一共只有1个人,就会出现这种情况。),那么渡河时间为 t a t_a ta
统计渡河总时间,输出。

【题解代码】

解法1:贪心
#include <bits/stdc++.h>
using namespace std;
#define N 1005
int main()
{
    int n, m, t[N];
    cin >> m;
    while(m--)
    {
        int sum = 0; 
        int i;
        cin >> n;
        for(i = 1; i <= n; ++i)
            cin >> t[i];
        sort(t+1,t+1+n);
        for(i = n; i >= 4; i-=2)
            sum += min(2*t[1]+t[i]+t[i-1], t[1]+2*t[2]+t[i]);
        if(i == 3)
            sum += t[1]+t[2]+t[3];
        else if(i == 2)
            sum += t[2];
        else//i == 1 如果只有一个人,那么就会出现这种情况 
            sum += t[1];
        cout << sum << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值