约瑟夫环

据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。

自杀游戏:给定总人数n,围成一个圈,报数为k的人死,从死的下一个人继续从1开始报数,杀到最后一个人存活。

模拟游戏过程:n=8,k=3

思路:从报数的人开始排序,报到3的人死

一开始人数和编号:1  2  3  4  5  6  7  8

杀第一人后:      6  7     1  2  3  4  5

杀第二人后:      3  4     5  6     1  2         

杀第三人后:         1     2  3     4  5

杀第四人后:         3     4        1  2

杀第五人后:               1        2  3

杀第六人后:               1        2       //因为是围成一个圈,1报1,2报2,轮到1报3,死!

杀第七人后:                        1

明显最后活下来的人是最开始的第七人,并且最后活着的这个人会被编号为1。

 

换一下编号,从0开始报数,报到2的人死,最后活着的人就是0。再模拟一下过程:

 

一开始人数和编号:0  1  2  3  4  5  6  7 

 

杀第一人后:      5  6     0  1  2  3  4

 

杀第二人后:      2  3     4  5     0  1          

 

杀第三人后:         0     1  2     3  4

 

杀第四人后:         2     3        0  1

 

杀第五人后:               0        1  2

 

杀第六人后:               0        1

杀第七人后:                        0

 

解题:从0反推回去,杀第六人后总人数还有2,假设想要返回的上一级的阶段的总人数为i,i=2;如何从0变成1,你会发现(0+k)%i=1

杀第六人后的1变成杀第五人后的1,此时i=3;(1+k)%i=1

杀第五人后的1变成杀第四人后的0,此时i=4;(1+k)%i=0

杀第四人后的0变成杀第三人后的3,此时i=5;(0+k)%i=3

杀第三人后的3变成杀第二人后的0,此时i=6;(3+k)%i=0

杀第二人后的0变成杀第一人后的3,此时i=7;(0+k)%i=3

杀第一人后的3变成原始状态时的6,此时i=8;(3+k)%i=6

因为是从0开始排序,所以活着的是第7人。

为什么这么玄妙,我也不知道,记得可以这样反推就行。

代码:

 

#include<iostream>
using namespace std;
int main()///约瑟夫裸题
{
    int n,k,num;
    while(cin>>n>>k)
    {
        num=0;      ///从最后一个存活着0开始反推
        for(int i=2;i<=n;i++)
            num=(num+k)%i;
        num++;          ///从0开始排序,结果+1
        cout<<num<<endl;
    }
    return 0;
}

 

 

 

再来看一道变相约瑟夫问题,半裸题

 

 

Let’s play a stone removing game.

Initially, n stones are arranged on a circle and numbered 1, …, n clockwise (Figure 1). You are also given two numbers k and m. From this state, remove stones one by one following the rules explained below, until only one remains. In step 1, remove stone m. In step 2, locate the k-th next stone clockwise from m and remove it. In subsequent steps, start from the slot of the stone removed in the last step, make k hops clockwise on the remaining stones and remove the one you reach. In other words, skip (k − 1) remaining stones clockwise and remove the next one. Repeat this until only one stone is left and answer its number. For example, the answer for the case n = 8, k = 5, m = 3 is 1, as shown in Figure 1.


Initial state

Step 1

Step 2

Step 3

Step 4

Step 5

Step 6

Step 7

Final state
 
Figure 1: An example game

Initial state: Eight stones are arranged on a circle.

Step 1: Stone 3 is removed since m = 3.

Step 2: You start from the slot that was occupied by stone 3. You skip four stones 4, 5, 6 and 7 (since k = 5), and remove the next one, which is 8.

Step 3: You skip stones 1, 2, 4 and 5, and thus remove 6. Note that you only count stones that are still on the circle and ignore those already removed. Stone 3 is ignored in this case.

Steps 4–7: You continue until only one stone is left. Notice that in later steps when only a few stones remain, the same stone may be skipped multiple times. For example, stones 1 and 4 are skipped twice in step 7.

Final State: Finally, only one stone, 1, is on the circle. This is the final state, so the answer is 1.

Input

The input consists of multiple datasets each of which is formatted as follows.

n k m

The last dataset is followed by a line containing three zeros. Numbers in a line are separated by a single space. A dataset satisfies the following conditions.

2 ≤ n ≤ 10000, 1 ≤ k ≤ 10000, 1 ≤ mn

The number of datasets is less than 100.

Output

For each dataset, output a line containing the stone number left in the final state. No extra characters such as spaces should appear in the output.

Sample Input
8 5 3
100 9999 98
10000 10000 10000
0 0 0
Sample Output
1
93
2019

 

题意:有n个石头,第一次拿掉第m个,从第(m+1)个开始算,随后每次拿掉第k个。依次输入n,k,m;

思路:返回最原始状态的时候把k改成m就行了

 

#include<stdio.h>
int main()
{
    int s,n,k,m,i;
    while(scanf("%d%d%d",&n,&k,&m)!=EOF&&(n+m+k))
    {
        s=0;
        for(i=2;i<n;i++)
            s=(s+k)%i;
        s=(s+m)%i;
        printf("%d\n",s+1);
    }
    return 0;
}

 

posted @ 2018-07-30 10:17  守林鸟  阅读(454)  评论(0编辑  收藏  举报