南邮NOJ生日聚会 与 约瑟问题(把异教徒投入海中排法)

<span style="font-family: Arial; font-size: 14px; background-color: rgb(255, 255, 255);">题目描述</span>

       今天是JacmY生日,他请大家吃饭,就这样,一行N个人来到了餐馆,大家吃吃喝喝,有说有笑,气氛甚欢,这时突然有人提议大家玩一个游戏,听罢规则后,就开始了游戏。

    游戏规则是这样的,吃饭的N个人围坐在桌子旁,JacmY是1号,沿着顺时针方向开始编号,2、3……N,然后由JacmY随机说一个数K(1 <= K <= N),从JacmY开始顺时针报数,“1,2……”,当有人报到K时,这个人从他的位置起来,先坐到了其他桌子,然后由下一个人重新从1开始报……就这样循环下去,每当有人报到K,这个人就离开了桌子,直到剩下这最后一个人,他非常倒霉的要接受大家的惩罚,就是罚酒一杯。JacmY有些郁闷了,K是由自己说的,万一说不好让自己受惩罚了,这个就有点不爽了,所以JacmY想请你帮忙找出哪些K会让他受罚,这样以来,JacmY就不用担心会喝太多酒了!



输入

 第一行一个整数T代表样例的组数

下面T行,每行一个整数N, 1 <= N<= 100;N如题目所述。

输出

       对于每组数据,第一行输出一个整数M,表示有M个这样的K会让JacmY受罚,接下来一行是M个整数,代表K分别取的哪些值,这些数字间用空格隔开。

样例输入

3
5
8
10

样例输出

1
4
2
2 6
1
8


#include <iostream>
#include<string.h>
#include<stdlib.h>
using namespace std;

int main()
{
	int T,N,k,index,i;
	int num;
	cin >> T;
	while(T--)
	{
		cin >> N;
		int *K= new int[N+1];
		for(i=0;i<=N;i++)                  //初始化数组K[N],用0,1分别标识能说,不能说,初始为都不能说
			K[i]=1;
		K[1]=0;                            //k=1 是可以说的,因为第一个走的就是自己
		num=1;                             //用num 记录可以说的k的个数
		for(k=2;k<=N;k++)
		{
			index = k-1;                   //用0,1,2,3,4,,,N-1,编号各位同学,jacmY是0
										   //用index标识当前被点到的同学
			for(i=1;i<=N-2;i++)
			{
				index = (index+k-1)%(N-i); //编号为k-1的同学走了,用k同学代替k-1同学,其后的同学编号全部-1。总人数-1。
				if(index == 0)             //表示jacmY被点到,在只剩1名同学前成功离开,可以说。
				{
					K[k] = 0;
					num ++ ;
					break;				   //退出循环,测试下一个k。
					
				}
			}
		}
		num = N - num;					   //更改num为所有不能说的k的个数
		printf("%d\n",num);
		for(i=1;i<=N;i++)
		{
			
			if(K[i]==1)                    //格式化输出
			{
				printf("%d",i);
				num--;
				if(num>0)
					printf(" ");
				if(num==0)
					printf("\n");
			}
			
		}
	}
	return 0;
}

详见注释。。。。PE错误,改了我好长时间。。。。。。

后来在书上看到一个 约瑟问题 跟此题相似,所以贴上来。
约瑟问题(把异教徒投入海中)
30名教徒一起乘船航行,基督徒和异教徒各15名,但因为遇到危险需要将一半人投入海中,方可得救。解决方法是30个人围城一圈,由第一个人报1,接着第二个人报数2,以此类推,报数为9的人被投入海。之后进行下一轮,下一个人接着从1开始报数,第9个人又被投入海中。按此规则循环,知道船上只剩下15个人为止,怎么排列这30个人才能将所有的异教徒投入海中?
#include <iostream>
using namespace std;

int main()
{
	int all[30];//所有人编号
	int yijiao[15];//异教徒编号
	int jidu[15];//基督徒编号
	int i,k,yijiao_count,yijiao_index,jidu_count;
	for (i=0;i<30;i++)
		all[i]=i+1;  //每人编号
	i=0;  //i为每次循环时计数变量
	k=0;  //k为按1,2...9报数时计数变量
	yijiao_count=0;  //投入海人数
	yijiao_index=0;  //存被投入海者数组的下标
	jidu_count=0;  //存在船上人编号数组的下标
	while (yijiao_count<15)//有15个投入海
	{
		if (all[i]!=0)//没被丢入海
			k++;
		if (k==9)
		{
			yijiao[yijiao_index]=all[i];
			yijiao_index++;
			all[i]=0;//被丢入海标志
			k=0;
			yijiao_count++;
		}
		i++;
		if (i==30)//到边界
			i=0;
	}
	for(i=0;i<30;i++)
	{
		if (all[i]!=0)
		{
			jidu[jidu_count]=all[i];
			jidu_count++;
		}
	}
	cout<<"被投入海的序号为:"<<endl;
	for(i=0;i<15;i++)
	{
		cout<<yijiao[i]<<"  ";
	}
	cout<<endl<<"留在船上的序号为:"<<endl;
	for(i=0;i<15;i++)
	{
		cout<<jidu[i]<<"  ";
	}
	cout<<endl;
	
	return 0;
}

all[i]=0;表示被投入了还,被排除的人只是被标记,座位(内存中的位置)依然保存。
按照这个思路,重写生日问题
/***************************
2015-5-02
给程序一个总人数
程序将计算出选择1-N之间任意一个数时,经过N-1次排除,最终剩下来的人是谁
***************************/

#include <iostream>
#include<string.h>
#include<stdlib.h>

using namespace std;

int main()
{
	int N,index,k,i,paichu_count;
	cin >> N;
	int *all=new int[N];
	
	for(index=1;index<=N;index++)
	{
		for(i=0;i<N;i++)			//给all[i]初始化
			all[i]=i+1;             //all[i]=i+1;实现了对内存0-N 编号1-N+1的转化,便于人去理解,编程
		i=0;
		k=0;
		paichu_count=0;
		while(paichu_count < N-1)
		{
			if(all[i]!=0)
				k++;
			if(k==index)
			{
				all[i]=0;
				k=0;
				paichu_count++;
			}
			i++;
			if(i==N)
				i=0;
		}
		for(i=0;i<N;i++)
		{	
			if(all[i]!=0)
//			if(all[i]!=0 && all[i]==1)        //只显示最后剩下编号为1的同学的话,就用这句。
				cout <<index <<"   "<< all[i] << endl;
		}
	}

	return 0;
}




你可能感兴趣的:(编程,C++,ACM,1069)