csdn英雄会(pongo)题解之报数游戏

题目详情:

有n个人编号1-n,按照顺时针方向围成一个圆圈。它们预先定义好两个整数x,y。先从1号顺时针方向开始报数,报到x的人出圈,再从x的逆时针方向的后一个人从1开始报数,报到y的人出圈,再从这个人的顺时针方向后一个人开始从1报数,报到x的人出圈,如此反复,直到最后剩下一个人为止,问最后剩下的那个人是几号?

比如n = 10, x = 3, y = 2,报数的过程如下


报数人 1, 2, 3  3出圈

报数人 2,1       1出圈

接着2,4 ,5       5出圈

接着4,2          2出圈

接着4,6,7       7出圈

接着6,4         4出圈

接着6,8,9      9出圈

接着8,6         6出圈

接着8,10,8    8出圈

剩余 10号。


输入n,x,y输出剩余的编号。

数据范围 1 < n <= 1000000,   1<=x,y<=1000000000。


----------------------------------------------------------------------------------------------

这道题是约瑟夫斯问题,自己可以先百度下约瑟夫斯问题。

我的解法是:

(1)每消去一个人后,将圆圈内的人重新按先后顺序依次编号(为了便于计算,我从0开始编号);例如0,1,2,3,4,5中消去2,那么剩余的0,1,3,4,5重新编号为0,1,2,3,4;

(2)每消去一个人时,记录下这个被消去人在当前圈中的索引;

(3)当圆圈内剩余一个人时结束,此时这个人在当前圈中的索引为LastID=0,其实问题也就是找这个人在原始圈中的相应索引LastID

(4)现在我们将最后一个被消去的人的索引插入到这个只有一个人的圈中,如果插入的索引值小于等于LastID(即在最后剩下人的前面),那么LastID加1;然后依次将倒数第二个、倒数第三个......消去人的索引插入到圈中,最后的LastID就是最后剩下人在原始圈中相应的索引。


代码如下:

#include <stdio.h>
#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
#include <stack>
using namespace std;
class Test {
public:
	static int remain (int   n,int   x,int   y){
		//题目的索引从1开始,我在计算过程中从0开始
		int res=0;
		int *delInd=new int[n-1];
		int start=0;//保存的是下一次消去时,开始的位置
		int flag=0;
		int j=0;
		for(int i=n;i>=2;--i){
			if(0==flag%2){			
				delInd[j]=(start+(x-1)%i)%i;
				start=(delInd[j]+i-1-1)%(i-1);				
			}else{
				delInd[j]=(start+i-(y-1)%i)%i;			
				start=delInd[j]%(i-1);
			}
			++j;
			++flag;
		}
		for(int i=n-2;i>=0;--i)
			if(delInd[i]<=res)++res;
        return res+1;
    }
};
//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main(){   
	clock_t t1=clock(); 
	cout<<Test::remain(10,3,2)<<endl;  
	cout<<Test::remain(9,4,3)<<endl;
	cout<<Test::remain(20000,4,3)<<endl;
	clock_t t2=clock();
    std::cout<<(t2-t1)/(double)CLOCKS_PER_SEC<<std::endl;
} 
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。


显然时间复杂度和空间复杂度都是O(n);


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值