• NOIP 2010 普及组初赛 第28题 过河问题


    【题目】

    NOIP 2010 普及组初赛试题 第28题 过河问题
    完善程序:
    (过河问题)在一个月黑风高的夜晚,有一群人在河的右岸,想通过唯一的一根独木桥走到河的左岸。在这伸手不见五指的黑夜里,过桥时必须借助灯光来照明,很不幸的是,他们只有一盏灯。另外,独木桥上最多承受两个人同时经过,否则将会坍塌。每个人单独过桥都需要一定的时间,不同的人需要的时间可能不同。两个人一起过桥时,由于只有一盏灯,所以需要的时间是较慢的那个人单独过桥时所花的时间。现输入 n(2≤n<100) 和这 n 个人单独过桥时需要的时间,请计算总共最少需要多少时间,他们才能全部到达河的左岸。

    例如,有 3 个人甲、乙、丙,他们单独过桥的时间分别为 1,2,4,则总共最少需要的时间为 7。具体方法是:甲、乙一起过桥到河的左岸,甲单独回到河的右岸将灯带回,然后甲、丙再一起过桥到河的左岸,总时间为 2+1+4=7。

    #include 
    using namespace std;
    
    const int SIZE = 100;
    const int INFINITY = 10000;
    const bool LEFT = true;
    const bool RIGHT = false;
    const bool LEFT_TO_RIGHT = true;
    const bool RIGHT_TO_LEFT = false;
    
    int n, hour[SIZE];
    bool pos[SIZE];
    
    int max(int a, int b)
    {
        if (a > b)
            return a;
        else
            return b;
    }
    
    int go(bool stage)
    {
        int i, j, num, tmp, ans;
        if (stage == RIGHT_TO_LEFT) {
            num = 0;
            ans = 0;
            for (i = 1; i <= n; i++)
                if (pos[i] == RIGHT) {
                    num++;
                    if (hour[i] > ans)
                        ans = hour[i];
                }
            if ([])
                return ans;
            ans = INFINITY;
            for (i = 1; i <= n - 1; i++)
                if (pos[i] == RIGHT)
                    for (j = i + 1; j <= n; j++)
                        if (pos[j] == RIGHT) {
                            pos[i] = LEFT;
                            pos[j] = LEFT;
                            tmp = max(hour[i], hour[j]) +[];
                            if (tmp < ans)
                               ans = tmp;
                            pos[i] = RIGHT;
                            pos[j] = RIGHT;
                        }
            return ans;
        }
        if (stage == LEFT_TO_RIGHT) {
            ans = INFINITY;
            for (i = 1; i <= n; i++)
                if ([]) {
                    pos[i] = RIGHT;
                    tmp =[];
                    if (tmp < ans)
                        ans = tmp;
                [];
                }
            return ans;
        }
        return 0;
    }
    
    int main()
    {
        int i;
            
        cin>>n;
        for (i = 1; i <=n; i++) {
            cin>>hour[i];
            pos[i] = RIGHT;
        }
        cout<<go(RIGHT_TO_LEFT)<<endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    请填空,完善程序。

    【题目考点】

    1. 深搜

    【解题思路】

    先看主函数,得知hour[i]表示第i人过河的时长,pos[i]是第i人的位置。看到cout<,可知go函数参数是传入过河的阶段(左到右还是右到左),返回值是所有人从右到左的总时长。
    看go函数if (stage == RIGHT_TO_LEFT)下的代码块,如果从右至左移动:

     num = 0;
     ans = 0;
     for (i = 1; i <= n; i++)
         if (pos[i] == RIGHT) {
             num++;
             if (hour[i] > ans)
                 ans = hour[i];
         }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    看这段代码可知,num是统计此时在河岸右边的人数,ans是求河岸右边的人中过河时间最长的人。
    接下来是:if ([ ① ]) return ans;,考虑什么情况下就应该直接返回结果了呢?河岸右侧最少应该是2人。(总人数大于等于2人,不可能只有右侧1人拿灯。如果右侧只有1人,还拿着灯,这个灯是谁送来的呢?)如果河岸右侧只有2人,这2人,就直接一起走到左岸了,时间为这两人中过河时间最长的时间,也就是ans。
    所以第①空填num <= 2num == 2
    然后看:

    ans = INFINITY;
    for (i = 1; i <= n - 1; i++)
        if (pos[i] == RIGHT)
            for (j = i + 1; j <= n; j++)
                if (pos[j] == RIGHT) {
                    pos[i] = LEFT;
                    pos[j] = LEFT;
                    tmp = max(hour[i], hour[j]) +[];
                    if (tmp < ans)
                       ans = tmp;
                    pos[i] = RIGHT;
                    pos[j] = RIGHT;
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    此时ans的值被重置为无穷大了,概念很可能就变了。而且下面也没再用到num。外层i从1到n-1,内层j从i+1开始到n,这是在枚举两个不同元素的下标与j。
    观看if语句下的结构,先让i,j走到左侧,再让i,j变回为右侧。学过搜索的同学看这个结构应该比较眼熟,这就是搜索回溯中的:设置状态与还原状态,在二者之间应该做的就是搜索下一层,所以这里该做深搜了。②处应该递归搜索下一层。
    返回的ans是所有人走到左侧的最小总时间,而观察代码可知ans是每次求出的tmp的最小值,那么tmp就是当前选中i,j从右侧走到左侧时,所有人从右侧到左侧的最小时间,max(hour[i], hour[j])是这一次i,j从右侧走到左侧的时间,②处应该是其余所有人从右侧到左侧的最短时间,应该递归调用go函数,下一次过河的阶段是从左到右,所以②处填写:go(LEFT_TO_RIGHT)

    if (stage == LEFT_TO_RIGHT) {
         ans = INFINITY;
         for (i = 1; i <= n; i++)
             if ([]) {
                 pos[i] = RIGHT;
                 tmp =[];
                 if (tmp < ans)
                     ans = tmp;
             [];
             }
         return ans;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    接着是从左侧到右侧的情况,我们直接想,从左侧到右侧,只是需要找一个过河时间最短的人把灯拿到右岸。
    ③处判断如果人在左侧:pos[i] == LEFT,那么让他到右侧,ans是tmp的最小值,返回的ans应该是所有人从右侧到左侧的最小时间,所以tmp应该是这一次让第i人从左侧到右侧,然后继续直到所有人完成过河需要的最小时间。所以tmp应该是i的过河时间加上下一阶段过河的最小时间,④处填hour[i]+go(RIGHT_TO_LEFT)
    最后还需要状态还原,⑤填pos[i] = LEFT

    【答案】

    ① num <= 2 / num < 3 / num == 2
    ② go(LEFT_TO_RIGHT)
    ③ pos[i] == LEFT / LEFT == pos[i]
    ④ hour[i] + go(RIGHT_TO_LEFT) / go(RIGHT_TO_LEFT) + hour[i]
    ⑤ pos[i] = LEFT

  • 相关阅读:
    YOLOv3~
    .split(“,“, -1) 和 .split(“,“) 的区别
    (蓝桥杯C/C++)——常用库函数
    Learn Prompt-ChatGPT 精选案例:学习各国语言
    鸿蒙即将抛弃Android,你还不来学习一下?
    企业如何拥有自己的超级 App?
    【K8s】部署自定义项目到K8s集群上运行
    正点原子嵌入式linux驱动开发——Linux INPUT子系统
    【每日一题】找到出现了K次的数
    Python:Unittest框架快速入门:用例、断言、夹具、套件、HTML报告、ddt数据驱动
  • 原文地址:https://blog.csdn.net/lq1990717/article/details/126173611