C++ 救济金发放(The Dole Queue, UVa 133) (《算法竞赛入门经典》例题4-3)

题目:n个人站成一圈,逆时针编号为1-n。有两个官员,A从1开始逆时针数,B从n开始顺时针数。每一轮中,官员A数到k个就停下来,官员B数到m个就停下来(可以停在同一个人身上)。接下来被选中的1或2个人被淘汰。

分析:本人认为本题思路破解关键在于两点。

        一是怎么看待淘汰与轮次。在每一轮中,官员A和官员B的挑选虽然是官员A先输出,但是同时发生的(即如果官员A先淘汰一个不会影响到官员B的选择)。并且每一新轮次的开始是沿着上一轮次定位到的位置开始的,并非重新开始(即不是每轮官员A都从开始数,官员B都是从尾开始数,而是接着上一轮继续)。

        二是怎么解决淘汰和轮次。如果每次将被淘汰的人踢出数组,则需要重新排数组,且最后需要输出的编号是原数组对应的编号,所有将被淘汰的人位置编号赋0是较好选择。由于每一轮次是按上一轮次继续的,则会出现超出数组范围需要从头开始的情况,这种情况需要特殊处理一下(方法不唯一,本人方法略繁琐,《算法竞赛入门经典》上的方法更高效)。

        本人代码如下,已通过vj测试!

#include<iostream>
#include<iomanip>
#include<string.h>
using namespace std;
int q[25];	//人员序列,定为全局变量,只需要值传递
int n;	//人员数量

int go(int p, int interval, int pace, int length)	//本次定位到要淘汰的位置,间隔,步长
{
	if (n == length)
	{
		if (pace == 1)
			p--;
		else
			p++;
	}
	while (interval--)
	{
		//保证回合轮次的循环正常进行
		if (pace == 1)
			p = (p + pace) % n;	//正序
		else
			p = ((p + pace) % n + n) % n;	//逆序
		if (q[p] == 0)	//跳过0的元素
			interval++;
	}
	return p;
}

int main()
{
	int k, m;	//正序间隔,逆序间隔
	while (cin >> n >> k >> m)
	{
		int length = n;
		if (n == 0 && k == 0 && m == 0)	//输入时0 0 0代表退出程序
			return 0;
		memset(q, 0, sizeof(q));
		for (int i = 1; i < n + 1; i++)	//人员编号,按正序1-n
			q[i-1] = i;	
		
		//接下来进入正题,传入数组,每次得到淘汰的1或2个数输出,淘汰的元素用0代替,之后跳过即可。
		//每次淘汰人员后length--,直到length=0开始下一次
		int p1 = 0, p2 = n - 1;
		while (length)
		{
			p1 = go(p1, k, 1, length);	//正序
			p2 = go(p2, m, -1, length);	//逆序
			if (p1 == p2)
			{
				cout << setw(3) << *right << setfill(' ') << p1 + 1;
				length--;
			}
			else
			{
				cout << setw(3) << *right << setfill(' ') << p1 + 1 << setw(3) << *right << setfill(' ') << p2 + 1;
				length -= 2;
			}
			q[p1] = q[p2] = 0;
			if (length)
				cout << ",";
		}
		cout << endl;
	}

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值