NOJ1069生日聚会——约瑟夫环

74 篇文章 0 订阅

生日聚会

Time Limit(Common/Java):20000MS/3000MS          Memory Limit:65536KByte
Total Submit:55            Accepted:16

Description

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

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

Input

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

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

Output

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

Sample Input

3
5
8
10

Sample Output

1
4
2
2 6
1
8

Source

Internet


分析:约瑟夫环。写法多样性。我的写法复杂了!我是按照以前经典算法里的模型写的。后面有一种更快的写法。

此题要注意输出问题。当个数为0是不需要多余的回车,输出k时结尾没有多余的空格。

#include<stdio.h>

//生日聚会

int main()
{
	int T, n;
	scanf("%d",&T);
	while(T --)
	{
		scanf("%d",&n);
		int num = 0, ans[100] = {0};

		for(int k=1;k<=n;k++)
		{
			int man[100] = {0};
			int count = 1;
			int i = 0, pos = -1;
			while(count <= n) 
			{
				do{
					pos = (pos+1) % n; // 环状处理
					if(man[pos] == 0)
						i++;
					if(i == k) // 报数为k了
					{ 
						i = 0;
						break;
					}
				} while(1);
				man[pos] = count;
				count ++;
			}
			if(man[0] == n) // 最后一个
				ans[num++] = k;
		}
		if(num != 0)
		{
			printf("%d\n",num);
			for(int i=0;i<num-1;i++)
				printf("%d ",ans[i]);
			printf("%d\n",ans[num-1]);
		}
		else
			printf("0\n");
	}

	return 0;
}

一种更快的写法:

#include<stdio.h>

//生日聚会

int main()
{
	int t, n, k;
	scanf("%d",&t);
	while(t --)
	{
		scanf("%d",&n);
		int num = 0, s = 0;
		int ans[10] = {0};
		for(k=1;k<=n;k++)
		{
			for(int i=1;i<=n;i++)
				s = (s+k)%i;
			if(s+1 == 1) 
				ans[num++] = k;
		}
		printf("%d\n",num);
		if(num != 0)
		{
			for(int i=0;i<num-1;i++) printf("%d ",ans[i]);
			printf("%d\n",ans[num-1]);
		}
	}

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值