<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;
}
按照这个思路,重写生日问题
/***************************
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;
}