题意翻译
题目描述 n(n<20)个人站成一圈,逆时针编号为 1~n。有两个官员,A从1开始逆时针数,B从n开始顺时针数。在每一轮中,官员A数k个就停下来,官员B数m个就停下来(两个官员有可能能停在同一个人上)。接下来被官员选中的1个或2个人离开队伍。
输入格式 输入n ,k ,m ,可能有多组数据,以 0 0 0结尾。
输出格式 输出每轮里被选中的人的编号(如果有两个人,先输出被A选中的)。输出的每个数应正好占3列。样例中的“ ␣ ”代表一个空格。
输入输出样例
输入 #1
10 4 3
0 0 0
输出 #1
␣␣4␣␣8,␣␣9␣
思路
设置一个数组,用来记录队伍中的编号是否出现过,设置一个变量,用来记录已经出现了多少编号了,设置两个变量,记录两个官员数到哪了.注意顺时针数的那个官员,可能会出现负数,变成负数则加上n.
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int n;
int k;
int m;
int que[25];
while (1) {
scanf("%d%d%d", &n, &k, &m);
// 退出条件
if (n == 0) {
return 0;
}
int num = 0;
// 注意初始化
memset(que, 0, sizeof(que));
int x = 0;
int y = n - 1;
int q;
// 循环退出条件,所有编号都出现
while (num < n) {
int i = 0;
while (i < k) {
// 数到已经出现过的编号则跳过,数下一个
if (que[x % n] == 1) {
x++;
} else {
x++;
i++;
}
}
// 注意占三列,且这里不要写que[(x - 1) % n] = 1,需要放到一轮猜完后
printf("%3d", (x - 1) % n + 1);
int j = 0;
while (j < m) {
if (y < 0) {
y = y + n;
}
if(que[y % n] == 1) {
y--;
} else {
y--;
j++;
}
}
// 还需要进行一次判断
if ((y + 1) % n != (x - 1) % n) {
printf("%3d", (y + 1) % n + 1);
que[(y + 1) % n] = 1;
num++;
}
que[(x - 1) % n] = 1;
num++;
// ','和回车出现的判断
if (num != n){
printf(",");
} else {
printf("\n");
}
}
}
return 0;
}
紫书上给了一种更加简洁的写法
#include <stdio.h>
#include <string.h>
#define maxn 25
int n, k, m;
int a[maxn];
// 返回移动到哪,使用一个d变量,完成了两种移动方式的
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() {
while (scanf("%d%d%d", &n, &k, &m) == 3 && n) {
for (int i = 1; i <= n; i++) {
a[i] = 1;
}
a[0] = 0;
int left = n;
int p1 = n;
int p2 = 1;
while (left) {
p1 = go(p1, 1, k);
p2 = go(p2, -1, m);
printf("%3d", p1);
left--;
if (p1 != p2) {
printf("%3d",p2);
left--;
}
a[p1] = a[p2] = 0;
if (left) {
printf(",");
}
}
printf("\n");
}
return 0;
}