救济金发放(The Dole Queue, UVa 133)

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列。

#include
#include
#include
#define MAXN 81
using namespace std;

int n, k, m;
int A(int arr[], int start1, int k) //A返回从start1开始逆时针数停下的人 
{
	int i;
	for(i = start1; ; i = (i+1) % n)
	{
		if(arr[i] == 0) continue;
		else if(k == 1) break;
		else k--;
	}
	return i+1;
}
int B(int arr[], int start2, int m) //B返回从start2开始顺时针数停下的人 
{
	int i;
	for(i = start2; ; i = i == 0 ? n-1 : i-1 )
	{
		if(arr[i] == 0) continue;
		else if(m == 1) break;
		else m--;
	}
	return i+1;
}

int main()
{
	scanf("%d %d %d", &n, &k, &m);
	int n1 = n, start1 = 0 , start2 = n-1;
	int arr[n] = {0};
	for(int i = 0; i < n; i++)
		arr[i] = i+1;
	
//	print(arr, n);
	while(n1!= 0)		
	{
		int a = A(arr, start1, k), b = B(arr, start2, m);
		if(a == b)
		{
			n1 -= 1;
			printf("%3d\n", a);
			arr[a-1] = 0; //被选中的位置设为零 
			start1 = a-1, start2 = a-1;// 重置起始位置 
//			print(arr, n);
		}
		else
		{
			n1 -= 2;
			printf("%3d %3d\n", a, b);
			arr[a-1] = 0, arr[b-1] = 0;//被选中的位置设为零 
			start1 = a-1, start2 = b-1;// 重置起始位置 
//			print(arr, n);
		}
		
	}
} 

自己写的代码还是存在可以改进的地方,两个函数A和B很相似,都是逐步寻找第k个人,唯一不同的是寻找方向不同,因此可以考虑合并为一个函数,增加一个形参步长d代表方向,d=1表示逆时针,d=2表示顺时针。

int go(int p, int d, int t) {
while(t--) {
do { p = (p+d+n-1) % n + 1; } while(a[p] == 0); //走到下一个非0数字
}
return p;
}

改进的防溢出操作也写得很巧妙。

你可能感兴趣的:(每天一道算法题,算法,数据结构)