题目: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;
}