約瑟夫問題系列

Josephus問題:
假設n個競賽者排成一個環形,依次順序編號1,2,…,n。從某個指定的第1號開始,沿環計數,每數到第m個人就讓其出列,且從下一個人開始重新計數,繼續進行下去。這個過程一直進行到所有的人都出列爲止。最後出列者爲優勝者。
無論是用鏈表實現還是用數組實現來解約瑟夫問題都有一個共同點:要模擬整個遊戲過程,不僅程序寫起
來比較麻煩,而且時間複雜度高達O(nm),當n,m非常大(例如上百萬,上千萬)的時候,幾乎是沒有辦法在短時間內出結果的。注意到原問題僅僅是要求出最後的勝利者的序號,而不是要模擬整個過程。因此如果要追求效率,就要打破常規,實施一點數學策略。

爲了討論方便,先把問題稍微改變一下,並不影響原意:

問題描述:n個人(編號0~(n-1)),從0開始報數,報到(m-1)的退出,剩下的人繼續從0開始報數。求勝利者的編號。

我們知道第一個人(編號一定是m%n-1) 出列之後,剩下的n-1個人組成了一個新的約瑟夫環
(以編號爲k=m%n的人開始):
k k+1 k+2 ... n-2, n-1, 0, 1, 2, ... k-2
並且從k開始報0。

現在我們把他們的編號做一下轉換:
k     --> 0
k+1   --> 1
k+2   --> 2
...
...
k-2   --> n-2
k-1   --> n-1

變換後就完完全全成爲了(n-1)個人報數的子問題,假如我們知道這個子問題的解:例如x
是最終的勝利者,那麼根據上面這個表把這個x變回去不剛好就是n個人情況的解嗎?變
回去的公式很簡單:x'=(x+k)%n

如何知道(n-1)個人報數的問題的解?顯然,只要知道(n-2)個人的解就行了。(n-2)個人的解
呢?當然是先求(n-3)的情況 ---- 這顯然就是一個倒推問題!

遞推公式:

令f[i]表示i個人玩遊戲報m退出最後勝利者的編號,最後的結果自然是f[n]

遞推公式
f[1]=0;
f[i]=(f[i-1]+m)%i; (i>1)

有了這個公式,我們要做的就是從1-n順序算出f[i]的數值,最後結果是f[n]。因爲實際生
活中編號總是從1開始,我們輸出f[n]+1

由於是逐級遞推,不需要保存每個f[i],程序也是很簡單:

#include <stdio.h>
main()
{
  int n, m, i, s=0;
  printf ("N M = "); 
  scanf("%d%d", &n, &m);
  for (i=2; i<=n; i++) s=(s+m)%i;
  printf ("The winner is %d/n", s+1);
}



這個算法的時間複雜度爲O(n),相對於模擬算法已經有了很大的提高。算n,m等於一百萬,一千萬的情況不是問題了。可見,適當地運用數學策略,不僅可以讓編程變得簡單,而且往往會成倍地提高算法執行效率。
以上轉自:http://blog.csdn.net/zouqibo/article/details/5681466

接下來我們來看兩道POJ的題:
1.The Joseph's problem is notoriously known. For those who are not familiar with the problem, among n people numbered 1,2...n, standing in circle every mth is going to be executed and only the life of the last remaining person will be saved. Joseph was smart enough to choose the position of the last remaining person, thus saving his life to give the message about the incident.

Although many good programmers have been saved since Joseph spread out this information, Joseph's cousin introduced a new variant of the malignant game. This insane character is known for its barbarian ideas and wishes to clean up the world from silly programmers. We had to infiltrate some the agents of the ACM in order to know the process in this new mortal game.

In order to save yourself from this evil practice, you must develop a tool capable of predicting which person will be saved.

The Destructive Process

The persons are eliminated in a very peculiar order; m is a dynamical variable, which each time takes a different value corresponding to the prime numbers' succession (2,3,5,7...). So in order to kill the ith person, Joseph's cousin counts up to the ith prime.

Input

It consists of separate lines containing n [1..3501], and finishes with a 0.

Output

The output will consist in separate lines containing the position of the person which life will be saved.

Sample Input
6
Sample Output
4

舉個例子,6個人分別編號1 2 3 4 5 6,2號出隊,剩下1 3 4 5 6,然後從3號開始循環往後數3下,5號出隊,剩下1 3 4 6,然後從6號循環往後數5下,6號出隊,剩下1 3 4,然後從1號開始循環往後數7下,1號出隊,剩下3 4,然後從3號開始循環往後數11下,3號出隊,最後剩下4.
我們可以模仿上面程序,只是m在動態變化,我們先用數組存儲連續的素數。然後在循環中不斷更換m值。代碼如下:
#include <iostream>
#include <cmath>
using namespace std;
bool is_prime(int x)
{
    int i;
    int y=sqrt(x);
    for(i=2;i<=y;i++)
    {
        if(x%i==0)
        {
            return false;
        }
    }
    return true;
}
int main()
{
    int i=3,j=2,n,prime[3501];
    prime[1]=2;
    for(i=3;j<3501;i+=2)
    {
        if(is_prime(i))
        {
            prime[j++]=i;
        }
    }
    while((cin>>n)&&n)
    {
        int s=0;
        j=n-1;
        for(i=2;i<=n;i++)
        {
            s=(s+prime[j--])%i;
        }
        cout<<s+1<<endl;
    }
    return 0;
}

2.

The Joseph's problem is notoriously known. For those who are not familiar with the original problem: from among n people, numbered 1, 2, ..., n, standing in circle every mth is going to be executed and only the life of the last remaining person will be saved. Joseph was smart enough to choose the position of the last remaining person, thus saving his life to give us the message about the incident. For example when n = 6 and m = 5 then the people will be executed in the order 5, 4, 6, 2, 3 and 1 will be saved.

Suppose that there are k good guys and k bad guys. In the circle the first k are good guys and the last k bad guys. You have to determine such minimal m that all the bad guys will be executed before the first good guy.

Input

The input file consists of separate lines containing k. The last line in the input file contains 0. You can suppose that 0 < k < 14.

Output

The output file will consist of separate lines containing m corresponding to k in the input file.

Sample Input

3
4
0
Sample Output
5
30
執行k次後,還剩下k人,這k個人編號分別爲0,1,2...k-1,將他們轉化到2k個人中的編號,編號必須在1到k之間。我們可以遍歷m,找到一個符合要求的m值,其中m%2k>k或者m%2k==0.代碼如下:
#include <iostream>


using namespace std;


int main()
{
    int k;
    int a[15];
    a[11]=459901;
    a[12]=1358657;
    a[13]=2504881;
    a[14]=13482720;
    while(cin>>k&&k)
    {
        if(k>=11)
        {
            cout<<a[k]<<endl;
            continue;
        }
        int s,i,m,node,flag;
        for(m=k+1;;m++)
        {
            flag=1;
            if((m%(2*k)>k)||(m%(2*k)==0))
            {
                for(node=0;node<k;node++)
                {
                    s=node;
                    for(i=k+1;i<=2*k;i++)
                    {
                        s=(s+m)%i;
                    }
                    if((s+1)>k)
                    {
                        flag=0;
                        break;
                    }
                }
            }
            else
            {
                m+=k-1;
                continue;
            }
            if(flag)
            {
                cout<<m<<endl;
                break;
            }
        }
    }
    return 0;
}
由於當k大於等於11時該程序會超時,所以只能先算出k=11,12,13,14時的m值...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章