题目描述
为了缩短领救济品的队伍,NNGLRP决定了以下策略:每天所有来申请救济品的人会被放在一个大圆圈,面朝里面。选定一个人为编号 1 号,其他的就从那个人开始逆时针开始编号直到 N。一个官员一开始逆时针数,数 k 个申请者,然后另一个官员第 N 个始顺时针方向数 m 个申请者,这两个人就被送去再教育。如果两个官员数的是同一个人,那个人则被送去从政,然后2个官员再在剩下的人里面继续选直到没人剩下来,注意两个被选 中的人是同时走掉的,所以就有可能两个官员选中一个人。
Input
输入含有多组测试资料,每组测试资料一列含有三个数 N,k 和 m(k, m > 0,0<N<20)。 当输入为 0 0 0 代表输入结束。
Output
对每组测试资料输出一列。输出被选中的申请者的编号顺序(一对一对的)。每个数的宽度为 3 。每一对前面的那个编号为逆时针数的官员选出的,后面的那个编号为顺时针数的官员选出的(但是如果这2个官员选出同一个人,那就只会有一个编号)。每一对 之间以逗号分开。格式请参考Sample Output。
Sample Input
10 4 3
13 17 42
7 8 47
0 0 0
Sample Output
4 8, 9 5, 3 1, 2 6, 10, 7
4 11, 10 1, 8 6, 13 7, 3, 5 12, 9 2
1 3, 5 7, 2 4, 6
#include <iostream>
int n, k, m, a[25];
int go(int p, int d, int t)
{
while (t--)
{
do
{
p = (p + d + n - 1) % n + 1; //重要!
} while (a[p] == 0);
}
return p;
}
int main()
{
using namespace std;
while ((cin>>n>>m>>k)&&(n&&k&&m))
{
for (int i = 1; i <= n; i++)
a[i] = i;
int left = n; //计剩余人数
int p1 = n, p2 = 1;
while (left)
{
p1 = go(p1, 1, k); //逆时针
p2 = go(p2, -1, m); //顺时针
cout<<a[p1];
left--;
if (p2 != p1)
{
cout<<" "<<a[p2];
left--;
}
a[p1] = a[p2] = 0; //出队后赋值0
if (left)
cout<<",";
}
cout<<endl;
}
return 0;
}
摘录:
至于下一个位置为什么是p = (p + n + d) % n.其实很简单。因为我们是一步步走的,所以只有两种边界情况。假设当前位置是p(0 = <p<n),
第一种边界:p + 1 > n - 1,即 p + 1此时应该是到达0位置,但此时p + 1 = n,如果我们取余数,则 (p + 1) % T = 0, T = n(T表示这个圆圈的周期大小)。
刚好能符合,又因为T = n,所以(P + T + 1) % T还是不变的。
第二种边界 : p - 1 < 0, 即 p - 1此时的值是 - 1,对于这种情况可以反过来看,它是向后退后1个单位,可以看成向前走T - 1个单位即p - 1 等效于 p + T - 1
,我们要等到此时的位置,再去余,(P + T - 1) % T。