【初赛题解】NOIP 2010 普及组初赛 第28题 过河问题

【题目】

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

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

#include <iostream>
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. 深搜

【解题思路】

先看主函数,得知hour[i]表示第i人过河的时长,pos[i]是第i人的位置。看到cout<<go(RIGHT_TO_LEFT)<<endl;,可知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];
     }

看这段代码可知,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;
            }

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

接着是从左侧到右侧的情况,我们直接想,从左侧到右侧,只是需要找一个过河时间最短的人把灯拿到右岸。
③处判断如果人在左侧: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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: NOIP2008 普及复赛试是中国国家信息学奥林匹克选手选拔赛的试之一,比较经典且有一定难度。以下是对该试的解答。 本次试主要有三道目,分别是小数排列、整数取位和检测型括号序列。 小数排列这道要求给定一个正整数 n,求小于 n 的所有符合要求的小数的个数。解决这一的方法是利用排列合的知识,找出符合要求的小数的模式并计算其个数。具体的代码实现就需要对 n 进行拆分,计算个位数、十位数和百位数的可能情况并相乘即可得到结果。 整数取位这道要求给定一个整数 n 和一个非负整数 m,求 n 的第 m 个数字。解决这一的方法是将整数 n 转化为字符串,然后通过字符串的索引来获取第 m 个数字。 检测型括号序列这道要求判断给定的一个仅包含左右括号的字符串是否是合法的括号序列。解决这一的方法是使用栈的数据结构,遍历字符串,对于每个遇到的左括号,将其压入栈中;对于每个遇到的右括号,检查栈顶元素是否为对应的左括号,若是,则弹出栈顶元素,否则返回不合法。 以上就是对 NOIP2008 普及复赛试的简要解答,其中涉及到的算法和数据结构是编程中比较常见的基础知识,通过理解和掌握这些知识,可以帮助我们更好地解决类似的编程问。 ### 回答2: NOIP(全国青少年信息学奥林匹克竞赛)是中国举办的一项信息学竞赛活动。2008年,NOIP举办了普及复赛。以下是对该年度复赛试进行的解答。 复赛试一共有三大目,分别涉及到图的遍历、数学运算和字符串处理。 第一是关于图的遍历的。目给出一张有向图和一个起始节点,要求按照拓扑排序的原则遍历整个图,并输出遍历的结果。拓扑排序是一种将有向无环图的顶点进行排序的算法,具体实现可以使用DFS或者BFS。根据目给出的起始节点,我们可以使用DFS从该节点开始遍历图,并使用一个栈来存储遍历的结果。 第二是一个数学运算的目,要求计算一个给定数的乘方结果的各位数字之和。这可以通过将给定数转化为字符串,然后对字符串中的每位数字进行相加来解答。也可以将给定数分解为各位数字相加的形式。具体实现上可以使用循环或者递归的方式。 第三是一个关于字符串处理的任务,要求将输入字符串中的数字字符提取出来,并计算所有数字的平均值。这个问可以通过遍历字符串的方式来解决。对于每个字符,我们判断是否为数字字符,是的话就将其转换为数字并累加到一个总和上。最后将总和除以数字字符的个数,得到平均值。 总体来说,NOIP 2008 普及复赛试涵盖了图的遍历、数学运算和字符串处理的内容。通过解答这些问,可以增强对这些概念的理解,并提升解决实际问的能力。 ### 回答3: NOIP2008普及复赛试是一道考察动态规划和递归思想的目。目给出了一个整数n,要求计算出整数1到n的所有排列中,满足以下条件的排列的个数: 1.相邻个数的差的绝对值不能等于1; 2.排列中的数不能重复。 首先,我们需要定义一个函数f(n),表示整数1到n的满足条件的排列的个数。我们可以将这个问转化为子问,即如何计算f(n-1)和f(n-2)等。 根据目要求,我们可以发现f(n)的值由部分成:一部分是以n结尾的满足条件的排列的个数,另一部分是不以n结尾的满足条件的排列的个数。 对于第一部分,即以n结尾的排列,我们可以将其分为种情况:n和n-2相邻,以及n和n-2不相邻。如果n和n-2相邻,那么有f(n-2)种情况。如果n和n-2不相邻,那么可以在以n-1结尾的排列后面加上n,所以有f(n-1)种情况。因此,以n结尾的满足条件的排列的个数为f(n) = f(n-1) + f(n-2)。 对于第二部分,即不以n结尾的排列,其个数就是f(n-1)。 所以,f(n) = f(n-1) + f(n-2) + f(n-1) = 2*f(n-1) + f(n-2)。 基本情况是当n=1时,满足条件的排列只有1个,即f(1)=1;当n=2时,满足条件的排列有2个,即f(2)=2。 通过递推,可以得到整个解空间中满足条件的排列的个数。 在编程实现时,可以使用动态规划来解决这道。先定义一个大小为n+1的数dp,dp[i]表示整数1到i的满足条件的排列的个数。然后,通过循环从3到n,依次计算dp[i]的值,最后返回dp[n]即可。 总结起来,这道是通过递推和动态规划的思想来计算满足条件的排列的个数。通过定义状态转移方程,将大问转化为小问,最后通过循环计算得出结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值