约瑟夫问题

UVA133 救济金发放

题目描述

n n n 个人站成一圈,逆时针编号为 1 ∼ n 1\sim n 1n。有两个官员, A \text{A} A 1 1 1 开始逆时针数, B \text{B} B n n n 开始顺时针数。

在每一轮中,官员 A \text{A} A k k k个就停下来,官员 B \text{B} B m m m 个就停下来(两个官员有可能能停在同一个人上)。接下来被官员选中的 1 1 1 个或 2 2 2个人离开队伍。

输出离开队伍的顺序,如果有两个人,先输出 A 选中的。

输入格式

本题多测

对于每组数据,输入共一行,输入 n , k , m n,k,m n,k,m

数据以 0   0   0 \texttt{0 0 0} 0 0 0 结尾。

输出格式

对于每组数据,输出一行,为离开队伍的顺序。

输出的每个数应正好占 3 3 3 列。

样例中的“ ␣ ”代表一个空格。

输入输出样例 #1

输入 #1

10 4 3
0 0 0

输出 #1

␣␣4␣␣8,␣␣9␣␣5,␣␣3␣␣1,␣␣2␣␣6,␣10,␣␣7

说明/提示

数据范围

对于 100 % 100\% 100% 的数据,满足 0 < n < 20 00<n<20 0 < k , m 00<k,m

方法一:

使用数组或列表模拟队列
使用一个数组/向量来保存还留在队列中的人。
每次在当前列表中找到要被淘汰的人的位置:
第一次顺时针找第 k 个;
第二次逆时针找第 m 个(注意是反方向);
删除该人并记录其编号;
直到队列为空为止。

关键点:

如何高效地处理“环”结构?可以用取模 % 来实现循环查找;
注意删除元素后,索引的变化;
注意方向变化(正向 vs 反向)。

int go(int p, int d, int t)//寻找下一个选中的人
{
    while (t--)
    {
        do {
            p = (p + d + n - 1) % n + 1;
        } while (arr[p] == 0);
    }
    return p;
}

上面的代码是解决约瑟夫问题的关键
p:代表当前位置
d是代表移动的方向,1代表逆时针移动,-1代表顺时针移动,
t:要移动的步数(即第几个有效的人)
以10 4 3为例
初始A在第一个位置,需要数4次,分别是1,2,3,4
所以设置A的初始位置p=0,移动方向d = 1,移动次数 t = k = 4
初始B在第十个位置,需要数3次,分别是10,9,8
所以设置B的初始位置p = 11,移动方向 d = -1,移动次数 t = m =3
如果查找下一个位置没有人,也就是arr[p]==0,那就继续找下一个

p = (p + d + n - 1)%n + 1

这里加 n-1 的作用就是实现环形数组的循环,为什么不是+n而是n-1呢
因为%模运算后的结果在[ 0 , n-1 ],但是原数组的下标是 [ 1 , n ],也就是说原本的数组范围要先映射到模运算的数组范围,最后再还原成原数组下标,也就是+1 的原因。

代码

#include 
using namespace std;
int n, k, m;
int arr[25];
int go(int p, int d, int t)
{
    while (t--)
    {
        do
        {
            p = (p + d + n - 1) % n + 1;
        } while (arr[p] == 0);
    }
    return p;
}
int main()
{
    while (cin >> n >> k >> m)
    {
        if (!n && !k && !m)
            break;
        for (int i = 1; i <= n; i++)
            arr[i] = i;
        int res = n;
        int p1 = 0, p2 = n + 1;
        while (res)
        {
            p1 = go(p1, 1, k);
            p2 = go(p2, -1, m);
            arr[p1] = 0;
            res--;
            printf("%3d", p1);
            if (p2 != p1)
            {
                res--;
                arr[p2] = 0;
                printf("%3d", p2);
            }
            if (res)
                printf(",");
        }
        cout << endl;
    }

    return 0;
}

你可能感兴趣的:(CCFCSP训练,算法)