/* 此处借助九度oj上的题目!
- 题目描述:
-
每年六一儿童节,JOBDU都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为JOBDU的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为1的小朋友开始报数。每次喊到m的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续1...m报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到JOBDU名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?
- 输入:
-
输入有多组数据。
每组数据一行,包含2个整数n(0<=n<=1,000,000),m(1<=m<=1,000,000),n,m分别表示小朋友的人数(编号1....n-1,n)和HF指定的那个数m(如上文所述)。如果n=0,则结束输入。
- 输出:
-
对应每组数据,输出最后拿到大奖的小朋友编号。
*/
本质就是 约瑟夫环
我们一般的约瑟夫环的思路是:先建立环,然后在绕圈子,代码入如下:
#include <stdio.h>
typedef struct link
{
long id;
struct link * next;
}link, *p_link;
int main()
{
long n, m, count, i;
p_link head;
p_link tmp, t;
while( scanf("%ld", &n) != EOF )
{
if( !n )
{
break;
}
scanf("%ld", &m);
head = ( p_link )malloc( sizeof( link ) );
head->id = 1;
head->next = head;
t = head;
for( i = 1; i < n; i++ )
{
tmp = ( p_link )malloc( sizeof( link ) );
tmp->id = i + 1;
tmp->next = t->next;
t->next = tmp;
t = tmp;
}
count = 0;
tmp = head;
while( n > 1 )
{
count++;
if( count == m )
{
n--;
// printf("%d ", tmp->id);
count = 0;
tmp->id = tmp->next->id;
t = tmp->next;
tmp->next = t->next;
free( t );
}
else
{
tmp = tmp->next;
}
}
printf("%ld\n", tmp->id);
}
return 0;
}
当数据量很大的时候,上述算法消耗时间很多!所以改进如下!;
第一个人(编号一定是(m)%n) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环:
k k+1 k+2 ... n-2, n-1, n, 1, 2, ... k-2
(以编号为k = ( m +1)%n的人开始)
并且从k开始报1。
现在我们把他们的编号做一下转换:( 这是核心 )
k --> 1
k+1 --> 1
k+2 --> 3
...........................................
k-3 --> n-1
k-2 --> n
序列1: 1, 2, 3 … n-2, n-1, n
序列2:1, 2, 3 … k-1, k+1, …, n-2, n-1, n
序列3: k, k+1, k+2, k+3, …, n-2, n-1,n, 1, 2, 3,…, k-1
序列4: 1, 2, 3 …, 5, 6, 7, 8, …, n-3, n-2,n-1
成为了(n-1)个人报数的子问题,这里就是递归的思想!!!!
我们可以得到:
f[i] = ( f[i-1] + m) % i; ( i > 1 ) ( %i的原因是:因为每一次的i都是一个子问题,呵呵~ )
#include <stdio.h>
int main()
{
int n,m,i,r;
while(scanf("%d",&n) == 1)
{
if( !n )
{
break;
}
scanf("%d",&m);
r = 0;
for( i = 2; i <= n; i++ )
{
r = (r + m) %i; // 这里!!!!
}
printf("%d\n",r + 1);
}
return 0;
}