例题4-3--救济金发放

题目:
n(n<20)个人站成一圈,逆时针编号为1~n。 有两个官员,A从1开始逆时针数,B从n开始顺时针数。 在每一轮中,官员A数k个就停下来,官员B数m个就停下来(注意有可能两个官员停在同一个人上)。 接下来被官员选中的人(1个或者2个)离开队伍。输入n,k,m输出每轮里被选中的人的编号(如果有两个人,先输出被A选中的)。 例如,n=10,k=4,m=3,输出为4 8, 9 5, 3 1, 2 6, 10, 7。 注意:输出的每个数应当恰好占3列。

解析:
注意题目的描述及示例,可以看出:要想经过1号,就要从n号开始逆时针走;要想经过n号,就要从1号开始顺时针走;因此有:p1=n;p2=1;
图示如下:
在这里插入图片描述
对于环状问题,首先想到的就是求余的方法,假设起始点为p,那么每移动一个点的原始的写法是:

p=(p+1)%n;   //逆时针
p=(p-1)%n;   //顺时针

这样写有没有问题呢?我们需要考虑边界条件。 分为两种情况:
1.当逆时针移动时,考虑到n对n求余等于0的时候(因为圈内没有为0的点),此时p=(n-1);对应上面的示范为n=9。我们希望下一个元素求余等于10----这显然是不可能的,因为一个数对n求余,其结果x一定满足:0<=x<=(n-1)
如何解决这个问题呢?不妨让求余结果为(n-1),最终结果再加上1,就可以达到n了。
其实也就是用当前的值减一再对n进行求余,最终结果+1。 表达式:

p=(p+d-1)%n+1;	//其中d代表正一或负一(顺时针或者逆时针走)

2.当顺时针移动时,仍然考虑边界条件。当p=1时,移动后的下一个值应该到了10 。可是带入数据却发现余数为0 。回顾逆时针的情况,最终的结果可以由余9加1得到,余9显然在p=n-1时成立,因此表达式为:

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

观察上面两个表达式,发现他们的差别仅仅是d的符号不同–这可以用传不同参数来改变,以及差了一个周期n–对于求余来说,加减n的倍数最终的结果是不改变的。

代码:

#include<iostream>
#include<Windows.h>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<ctype.h>
#include<cmath>
#define MAXN 10000
using namespace std;
int n, k, m, a[MAXN];
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()
{
	int i, j, p1, p2;
	int left;
	while (scanf("%d%d%d", &n, &k, &m) == 3 && n)
	{
		p1 = n;p2 = 1;
		left = n;
		for (i = 1;i <= n;i++) a[i] = i;
		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");
	}
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/cprimesplus/article/details/83758693
今日推荐