约瑟夫斯问题及其编程

约瑟夫斯(Josephus)问题是一个出现在计算机科学数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。并且约瑟夫斯问题的描述是这样的:有n个囚犯站成一个圆圈,准备处决。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。

    然后问题是,给定了n和k,一开始要站在什么地方才能避免被处决?

    在约瑟夫斯问题里详细介绍了其中k=2时此问题的数学方式的解法,并得到了一个可以通过数学归纳法证明的定理:

如果n=2^m+l0\leq l<2^m,则f(n) = 2l+1

    比较令人眼前一亮的是这个结论的表现形式竟与整数n的二进制表示有关:即把n的第一位移动到最后,便得到f(n)。如果n的二进制表示为n=b_0 b_1 b_2 b_3\dots b_m,则f(n)=b_1 b_2 b_3 \dots b_m b_0。这点很容易证明:因n的第一位是1,并且其余位的十进制值就是l,然后左移一位(即2*l)再加上第一位的值就变成了f(n)的值了。

    接着使用动态规划的方法得到一般情况下(即k\neq 2)的递推公式:

当n=1时,f(1,k)=1

但n>1时,f(n,k)=(f(n-1,k)+k) \bmod n

此时先开始用数学方法来实现约瑟夫斯问题的编程,其代码如下(使用递推方式):

#include 

using namespace std;

int Josephus(int n, int k);

int main()
{
    int n, k, ret;
    cout << "Please Enter Two number(n, k): ";
    cin >> n >> k;
    
    ret = Josephus(n, k);
    
    cout << "The final winner is " << ret << endl;
    
    system("pause");
    return 0;
    }
    
    int Josephus(int n, int k)
    {
        int ret;
        if(1 == n)
             return ret = 1;
        else
        {
            ret = (Josephus(n-1, k) + k)%n;
            if(0 == ret)
                 ret = n;
            return ret;
            }
        }

当输入n=9、k=5时最后一个数是8,下图就是执行的结果:


注意:这里必须要对Josephus(n, k)的值进行判断,因Josephus(n-1, k)+k>=k>0,所以当Josephus(n, k)为0时,必须要将其赋值为n。(为什么是n而不是n*m?其中m是大于1的任意正整数)

例如注释掉上述代码中的这段代码:if(0 == ret) ret = n;

执行的结果如下所示:


此时结果是错误的,最后留下来的人应该是9才对。

同时这里的递推可以用循环来实现:

#include 

using namespace std;

int main()
{
    int n, k, ret = 1, i;
    cout << "Please Enter Two Number(n, k): ";
    cin >> n >> k;
    
    for(i = 2; i <= n; i++)
    {
          ret = (ret + k)%i;
          if(0 == ret)
               ret = i;
          }
          
    cout << "The final winner is " << ret << endl;
    
    system("pause");
    return 0;
    }

执行结果如下图所示:


接下来使用链表方法来实现:

这里解决这个问题的核心步骤(即程序的基本算法)是:
1)建立一个具有n个链接点而无头结点的循环链表;
2)确定第一个报数人的位置即m;
3)不断地从该链表中删除报数人处的链接点,直到该链表为空。
其代码如下:
#include 

using namespace std;

typedef struct node
{
        int data;
        struct node *next;
        }Node;
        
void createList(Node* &head, Node* &tail, int n);
void print(Node* &head);
void countPrint(Node* &head, Node* &tail, int k);

int main()
{
    Node *head, *tail;
    int n, k;
    cout << "Please Enter Two numbers(n, k): ";
    cin >> n >> k;
    
    createList(head, tail, n);
    print(head);
    countPrint(head, tail, k);
    
    system("pause");
    return 0;
    }
    
void createList(Node* &head, Node* &tail, int n)
{
     if(n < 1)
     {
          head = NULL;
          return;
          }
     head = new Node;
     head->data = 1;
     head->next = NULL;
     
     Node *p = head;
     for(int i = 2; i < n+1; i++)
     {
             p->next = new Node;
             p = p->next;
             p->data = i;
             p->next = NULL;
             }
     
     tail = p;
     p->next = head;
     }
     
void print(Node* &head)
{
     Node *p = head;
     
     do
     {
             cout << " " << p->data << " ";
             p = p->next;
             }while(p != head);
     cout << "\n";
     }
     
void countPrint(Node* &head, Node* &tail, int k)
{
     Node *cur = head;
     Node *pre = tail;
     
     int cnt = k;
     while(cur && cur != cur->next)
     {
               if(--cnt)
               {
                        pre = cur;
                        cur = cur->next;
                        }
               else
               {
                   pre->next = cur->next;
                   cout << " " << cur->data << " ";
                   delete cur;
                   cur = pre->next;
                   cnt = k;
                   }
               }
     if(cur == cur->next)
     {
                   cout << " " << cur->data << endl;
                   delete cur;
                   head = tail = NULL;
                   }
     }
执行结果如下图所示:

参考资料: http://blog.csdn.net/zhuimengzh/article/details/6727221


你可能感兴趣的:(C/C++常见面试题/算法)