UVA133
题目描述
为了缩短领救济品的队伍,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
首先是你每次在写程序之前,都要十分清除规则,题目中的人是围着一圈,而且第一个的左边是第N个人,也就是它是逆时针标号的。这个十分关键。
其次是go函数的实现,go函数是数过L个人,返回最后一个的位置。我并不赞同,某些版本数组是从1开始计数,因为这样对于表达式的表达十分不方便。你可以
自己尝试用1来做,会很不方便。就是因为go函数是这样一个函数,所以当我们在下一次迭代的时候的开始位置,一定是为那个人出去的位置,也就是a[i]=0的位置。
所以我们第一次迭代的位置,原本A是应该在位置0,B在位置n-1。这时候只能是A在n-1和B在0.(你可以用数学归纳法理解)。
1 #include <stdio.h>
2
3 #define MAXN 25
4 int n,k,m;
5 int a[MAXN];
6 int go( int p, int d, int k); // 数过k个人,开始位置p必须是数1时候的前一个位置。
7 int main() {
8 while (scanf("%d%d%d", &n, &k, &m) == 3 && n) {
9 for ( int i = 0; i < n; i++) {
10 a[i] = i + 1;
11 }
12 int left = n;
13 int pA = n-1, pB = 0;
14 int pANext,pBNext;
15 while (left) {
16 pA = go(pA, 1, k);//1表示逆时针,因为它是逆时针标号
17 pB = go(pB, -1, m);//-1表示顺时针
18 printf("%3d", pA + 1); left--;
19 if (pA != pB) { printf("%3d", pB + 1); left--;}
20 a[pA] = a[pB] = 0;
21 if (left) printf(",");
22 }
23 printf("\n");
24 }
25 return 0;
26 }
27 int go( int p, int d, int L) {
28 while (L--) {
29 do { p = (p+n+d)%n;} while(a[p] == 0);
30 }
31 return p;
32 }
解析:至于下一个位置为什么是p = (p+n+d)%n.其实很简单。因为我们是一步步走的,所以只有两种边界情况。假设当前位置是p(0=<p<n),
2
3 #define MAXN 25
4 int n,k,m;
5 int a[MAXN];
6 int go( int p, int d, int k); // 数过k个人,开始位置p必须是数1时候的前一个位置。
7 int main() {
8 while (scanf("%d%d%d", &n, &k, &m) == 3 && n) {
9 for ( int i = 0; i < n; i++) {
10 a[i] = i + 1;
11 }
12 int left = n;
13 int pA = n-1, pB = 0;
14 int pANext,pBNext;
15 while (left) {
16 pA = go(pA, 1, k);//1表示逆时针,因为它是逆时针标号
17 pB = go(pB, -1, m);//-1表示顺时针
18 printf("%3d", pA + 1); left--;
19 if (pA != pB) { printf("%3d", pB + 1); left--;}
20 a[pA] = a[pB] = 0;
21 if (left) printf(",");
22 }
23 printf("\n");
24 }
25 return 0;
26 }
27 int go( int p, int d, int L) {
28 while (L--) {
29 do { p = (p+n+d)%n;} while(a[p] == 0);
30 }
31 return p;
32 }
第一种边界: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。
对于情况一、二。可以归纳为(P+T+d)%T,当为顺时针是d取1,否则-1.