算法习题---4.3救济金发放(UVa133)
一:题目
(n<20 )个人站成一圈,逆时针编号为1~n。
有两个官员,A从1开始逆时针数,B从n开始顺时针数。
在每一轮中,官员A数k个就停下来,官员B数m个就停下来(注意有可能两个官员停在同一个人上)。接下来被选中的人离开队伍。 输入n,k,m输出每轮被选中的人的编号(如果有两个人,先输出A的)每个输出数字正好占3列。
二:实现思路
A从数组首部向后遍历(若是该位置还有人,则步数加一,否则不改变步数),当遍历到最后,则转回首部继续遍历。
B从数组尾部向前遍历(...),若是遍历到首部,则转回尾部继续向前遍历
三:代码实现
int member[21] = { 0 }; //为了方便理解,从下标1开始到下标20被利用 int n, k, m; /* input: int pos //当前位置 int dict //谁要走 int step //走的步数 output: 走之后的当前位置 */ int donate(int pos, int dict, int step) { while (step--) { do { pos = (pos + dict + n - 1) % n + 1; } while (member[pos]==0); } return pos; } void func04() { freopen("data.in", "r", stdin); freopen("data.out", "w", stdout); while (1) { //开始读取数据 scanf("%d%d%d", &n, &k, &m); if (n == 0 && k == 0 && m == 0) break; //结束 int pos1 = n, pos2 = 1, count = n; //将对应人数置为1,表示没有领取救济金 for (int i = 1; i <= n; i++) member[i] = 1; while (count) { pos1 = donate(pos1, 1, k); pos2 = donate(pos2, -1, m); printf("%3d", pos1); count--; if (pos1 != pos2) { printf("%3d", pos2); count--; } member[pos1] = member[pos2] = 0; if (count) printf(", "); } printf("\n"); } freopen("CON", "r", stdin); freopen("CON", "w", stdout); }
四:代码分析
(一):位置计算
pos = (pos + dict + n - 1) % n + 1;
当方向dict为正数时,pos + dict + n - 1 = pos+n
故pos = (pos+n)%n + 1向前走一步pos在(1,n)范围
当dict为负数时,pos + dict + n - 1 = pos+n-2
pos = (pos+n-2)%n+1 = pos - 2+1 = pos-1;向后走一步pos范围在(1,n)
(二):初始值
pos1 = n, pos2 = 1
我们传递到处理函数中的位置初值,应该是我们开始处理的前一个数据。
我们返回应该处理的数据的位置
五:应该将逻辑和数据分开《重点》
donate函数只复制找到我们需要处理的位置,main函数则继续数据修改。这样为我们后面的调试,和解耦合带来的好处