小孩排队问题

标签: 算法


初始问题:幼儿园小孩排成一行,但是男孩和女孩相邻会冲突,现在你是老师,每次只能调换相邻两个小孩的位置,要使男女冲突最少,至少要调换多少次?

输入情况: 用形如'BBBGGBB'的一个字符串表示队伍,'B'表示男孩,'G'表示女孩。

输入情况:最少的调换次数。

  目标是使男女冲突最少,可以理解为把男孩和女孩分开两边,左边全站男孩,右边全站女孩(或者左边全站女孩,右边全站男孩),那就只有中间的一个男孩一个女孩相邻了,显然这样冲突最少。

例如:'BBGBG' 变成 'BBBGG' 或者 ' GGBBB'

思路一:每次只能调换相邻两个小孩的位置,这个问题类似与冒泡排序,即每次调换两个冲突的小孩的位置,那么每次循环下来,就把一个小孩调换(移动)到最左边或者最右边,两重循环即可把所有孩子调换好,需要调换时使用计数器加一记录即可。

// 从右往左冒泡
// @s 小孩队列
// @move 该移动的小孩,'B'为男孩,'G'为女孩
public static int bubbleCount(char[] s, char move) {
    int count = 0;
    for (int i = 0; i < s.length - 1; i++) {
        for (int j = s.length - 1; j > i; j--) {
            // 若相邻两小孩性别不同,需要调换
            if (s[j] == move && s[j] != s[j - 1]) {
                swap(s[j], s[j - 1]);
                count++;
            }
        }
    }
    return count;
}

  这种方法使用了冒泡排序算法,复杂度为\(O(n^2)\),能算出移动男孩或者移动女孩的次数,且修改了原队列的值。

  要注意,只移动男孩或只移动女孩,有时并不是最优解。例如队列'BBBBBGB'时,如果移动男孩(这里统一指往左边移动),只需要将最右边的男孩往左移动1次。但移动的是女孩时,则需要调换5次。即实际上,调换男孩是调换次数最少的换法。所以要考虑这两种情况的最小值,即min(bubbleCount(s, 'B'), bubbleCount(s, 'G')

思路二:不改变原队列的值,只记录某个小孩右边的异性个数,该个数即该小孩需要调换(移动)的次数,问题是求得所有小孩移动次数的总和。

// 从左往右计数
// @s 小孩队列
// @move 该移动的小孩,'B'为男孩,'G'为女孩
public static int count(char[] s, char move) {
    int count = 0;
    for (int i = 0; i < s.length; i++) {
        if (s[i] == move) 
            for (int j = i + 1; j < s.length; j++) 
                if (s[j] != move) count++;
        return count;
    }
}

  这个方法循环计算了每个待移动的小孩右边的异性个数,复杂度为\(O(n^2)\)。有没有更加好的方法?我们观察到,从左到右计数时,异性的个数其实是重复计数的,所以我们可以使用一个变量存储起来,这时只需要循环一次即可解决问题,复杂度为\(O(n)\)

// 从左往右计数
// @s 小孩队列
// @move 该移动的小孩,'B'为男孩,'G'为女孩
public static int count(char[] s, char move) {
    int count = 0;
    // 不需移动的小孩个数
    int notMoveNum = 0;
    for (int i = 0; i < s.length; i++) {
        if (s[i] == move)
            count += notMoveNum;
        else
            notMoveNum++;
    }
    return count;
}

  实际上,这个方法记录的是某个小孩左边的异性个数。要记录右边的异性个数,只需要把循环条件反过来即可。
  和上面冒泡算法的情况相同,最后要从移动男孩还是移动女孩两种情况中,选出移动次数最少的一种,即min(count(s, 'B'), count(s, 'G')

转载于:https://www.cnblogs.com/banyu/p/6622272.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在银行服务窗口中,经常会出现多个人排队等待办理业务的情况,这就是银行排队问题。为了解决这个问题,可以使用MATLAB来进行模拟和分析。 首先,可以使用MATLAB的随机数生成函数来模拟客户到达银行窗口的时间间隔和每个客户办理业务所需的时间。可以使用指数分布来生成到达时间间隔,使模拟结果更符合实际情况。同样地,可以使用指数分布或其他分布来生成每个客户办理业务所需的时间。 然后,可以使用MATLAB的队列模型来模拟多个客户在排队等待服务的过程。可以创建一个队列对象,并根据不同的策略(如先来先服务或最短作业优先)来处理队列中的客户。在每个时间步中,将客户添加到队列中,并根据策略选择下一个要服务的客户。可以利用MATLAB的条件判断和循环语句来实现这个过程。 在模拟过程中,可以记录每个客户等待时间和总体等待时间的统计信息。可以使用MATLAB的统计工具箱来计算和分析这些数据,例如计算平均等待时间和等待时间的分布。 最后,可以通过修改模拟参数或策略,进行不同的实验和分析。例如,可以尝试不同的到达时间分布、不同的服务时间分布或不同的排队策略,以便获得更好的银行排队方案。 综上所述,MATLAB是一个强大的工具,可以用于模拟和分析银行排队问题。通过模拟客户的到达和服务过程,并分析等待时间和排队策略的统计数据,可以帮助银行优化服务效率,提高客户满意度。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值