题目回忆
N个人坐成一圈,按照1,2,3,…,N围成1圈,编号为K的人有K个物品。有2个人分别依次数数,一个按照顺时针,每R个人选出一位,比如R=3,则依次数出3、6;另一个按照逆时针,比如R=2,依次数出N-1、N-3。每个被数出的人退出游戏,并将物品交给下一个,顺时针/逆时针分别给K+1/K-1。游戏直到只剩下max(L,R)-1个人的时候结束。
输入
总人数N,顺时针间隔R,逆时针间隔L。
10 3 3
输出
物品最多的人的编号和物品数量。
7 46
解释:
首先3出局,将物品给4,然后8出局,将物品给7;6出局,物品给7,5出局,将物品给4。最后剩下1有9个,7有46个。输出“7 46”。
输入
10 2 2
输出
1 55
题解
难点是理解题目。因为到后面,要找实际上相邻的人,所以分别使用rihgt,nextl,left,nextr标记。编号的范围是1-N,所以需要%N,根据需要+/-N。用flag数组标记当前编号的人是否已经退出游戏。用v数组记录一个人拥有的物品数量。
代码写得有点繁琐。
#include<iostream>
#include<map>
#include<vector>
using namespace std;
vector<int> v;
vector<bool> flag;
int main(){
int n, r, l, minsize, size, right = 0, left = 1;
cin >> n >> r >> l;
v.resize(n + 1);
flag.resize(n + 1);
for(int i = 1; i <= n; i++){
v[i] = i;
flag[i] = true;
}
size = n;
minsize = max(r, l) - 1;
while(size > minsize){
int tmpl = 0, tmpr = 0, nextr, nextl;
while(tmpr < r){
right = right + 1;
if(right > n) right = right % n;
if(flag[right]) tmpr++;//找到实际上的顺时针数下去的第r个人
}
flag[right] = false;
nextr = right + 1;
if(nextr > n) nextr = nextr % n;
while(!flag[nextr]){
nextr++;
if(nextr > n) nextr = nextr % n;//寻找实际上的下一个顺时针的人
}
v[nextr] += v[right];
size--;
if(size > minsize){//每次寻找都确认是否达到最低人数要求
while(tmpl < l){
left = left - 1;
if(left < 1) left = left + n;
if(flag[left]) tmpl++;
}
flag[left] = false;
nextl = left- 1;
if(nextl < 1) nextl = nextl + n;
while(!flag[nextl]){
nextl--;
if(nextl<1) nextl = nextl + n;
}
v[nextl] += v[left];
}
size--;
}
//查找物品最多的人
int label, total = 0;
for(int i = 1; i <=n; i++){
if(flag[i]){
if(v[i] > total){
label = i;
total = v[i];
}
}
}
cout << label << ' ' << total;
return 0;
}