正文

约瑟夫环问题算法集锦2008-12-05 12:41:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/goal00001111/39774.html

分享到:

/*  Name: 约瑟夫环问题算法集锦   Copyright: 始发于goal00001111的专栏;允许自由转载,但必须注明作者和出处  Author: goal00001111搜集整理   Date: 03-12-08 18:14  Description: 有编号从1到N的N个人坐成一圈报数,报到M的人出局,下一位再从1开始, 如此持续,直止剩下一位为止,报告此人的编号X。输入N,M,求出X。 共搜集整理了7类10种算法,对于初学者和算法爱好者来说——看了绝对值! */#include<iostream>#include <time.h>using namespace std;int Fun_1(int n, int m);int Fun_2(int n, int m);int Fun_3(int n, int m);int Fun_4(int n, int m);int Fun_5(int n, int m);int Fun_6(int n, int m);int Fun_7(int n, int m);int Fun_8(int n, int m);int Fun_9(int n, int m);int Fun_10(int n, int m);int main(int argc, char* argv[]){    int n, m;    //cout << "Input max, step: ";//    cin >> n >> m;    time_t startTime;    time_t endTime;        time(&startTime);    for (int i=10; i<11; i++)       cout << Fun_1(i, 8);    time(&endTime);    cout << "No.1 time = " << difftime(endTime, startTime) << endl;        time(&startTime);    for (int i=10; i<11; i++)       cout << Fun_2(i, 8);    time(&endTime);    cout << "No.2 time = " << difftime(endTime, startTime) << endl;        time(&startTime);    for (int i=10; i<11; i++)       cout << Fun_3(i, 8);    time(&endTime);    cout << "No.3 time = " << difftime(endTime, startTime) << endl;        time(&startTime);    for (int i=10; i<11; i++)       cout << Fun_4(i, 8);    time(&endTime);    cout << "No.4 time = " << difftime(endTime, startTime) << endl;        time(&startTime);    for (int i=10; i<11; i++)       cout << Fun_5(i, 8);    time(&endTime);    cout << "No.5 time = " << difftime(endTime, startTime) << endl;        time(&startTime);    for (int i=10; i<11; i++)       cout << Fun_6(i, 8);    time(&endTime);    cout << "No.6 time = " << difftime(endTime, startTime) << endl;        time(&startTime);    for (int i=10; i<11; i++)       cout << Fun_7(i, 8);    time(&endTime);    cout << "No.7 time = " << difftime(endTime, startTime) << endl;        time(&startTime);    for (int i=10; i<11; i++)       cout << Fun_8(i, 8);    time(&endTime);    cout << "No.8 time = " << difftime(endTime, startTime) << endl;        time(&startTime);    for (int i=10; i<11; i++)       cout << Fun_9(i, 8);    time(&endTime);    cout << "No.9 time = " << difftime(endTime, startTime) << endl;        time(&startTime);    for (int i=10; i<11; i++)       cout << Fun_10(i, 8);    time(&endTime);    cout << "No.10 time = " << difftime(endTime, startTime) << endl;        system("pause");    return 0;}//采用了设置个人编号和计数器方法,屏蔽已经出圈人的位置 int Fun_1(int n, int m){    int *a = new int[n]; //设置一个数组用来存储n个人的编号        for (int i=0; i<n; i++) //注意C语言中的数组下标从0开始     {        a[i] = i + 1;    }     int s = 1; //累计出圈的人数,设初值为1,那最后一个就不用出圈了     int count = 0; //计数器,数到m就归零         while(s < n)    {        for (int i=0; i<n; i++)//只要还有超过1个人在圈里,就不断的遍历数组         {            if (a[i] == 0) //编号为0,表示此人已经出圈                 continue;            count++;    //报一次数            if (count == m) //报数为m的那个人出圈            {                a[i] = 0; //设此位置编号为0,表示此人已经出圈                 ++s; //出圈人增加一个                 count = 0; //计数器归零            }         }    }    int pos;    for (int i=0; i<n; i++) //遍历数组,看看还有谁没有出圈,找的就是他     {        if (a[i] != 0)        {            pos = a[i];            break;        }    }     delete []a;    return pos;}//采用了计数器方法,也使用了数组,但数组中存储的不是个人的编号,而是是否出圈的信息 int Fun_2(int n, int m){    int *a = new int[n]; //设置一个数组用来存储n个人是否出圈,1表示在圈内,0表示在圈外         for (int i=0; i<n; i++) //首先设置所有人都在圈内     {        a[i] = 1;    }     int s = 1; //累计出圈的人数,设初值为1,那最后一个就不用出圈了     int count = 0; //计数器,数到m就归零         while(s < n)    {        for (int i=0; i<n; i++)//只要还有超过1个人在圈里,就不断的遍历数组         {            count += a[i]; //若a[i]=0,就直接跳过了             if (count == m) //报数为m的那个人出圈            {                a[i] = 0; //设此位置编号为0,表示此人已经出圈                 ++s; //出圈人增加一个                 count = 0; //计数器归零            }         }    }    int pos;    for (int i=0; i<n; i++) //遍历数组,看看还有谁没有出圈,找的就是他     {        if (a[i] == 1)        {            pos = i + 1;            break;        }    }     delete []a;    return pos;}//采用了数组队列的方法,每出圈一个人,就从队列中删除 int Fun_3(int n, int m){    int *a = new int[n]; //设置一个数组用来存储n个人的编号        for (int i=0; i<n; i++) //注意C语言中的数组下标从0开始     {        a[i] = i + 1;    }     int front=0, rear=n-1;  //对头front指向第一个元素,队尾rear指向最后一个元素     int count = 0; //计数器,数到m就归零         while ((front) != (rear))//当队列元素还有一个,注意这里不需要队列为空,而是留一个元素    {        count++;    //报一次数        if (count == m) //报数为m的那个人出圈        {            front = ++front % n; //将该元素从队列中删除             count = 0; //计数器归零        }          else //否则把对头的元素放到队尾去         {            rear = ++rear % n;//队尾前移,以存储从对头转来的元素             a[rear] = a[front];            front = ++front % n; //对头前移,指向新的元素         }                }    delete []a;    return a[front];  }//很巧妙的方法,不用屏蔽位置, 需要计数器,采用数组,但数组中存储的不是本人的编号,而是是下一个人的编号  int Fun_4(int n, int m){    int *a = new int[n]; //设置一个数组用来存储n个人是否出圈,1表示在圈内,0表示在圈外         for (int i=n-2; i>=0; i--) //存储下一个人的编号     {        a[i] = i + 1;    }     a[n-1] = 0; //最后一个人的下一位是第一个人     int s = 0; //累计出圈的人数,设初值为1,那最后一个就不用出圈了     int count = 0; //计数器     int pos; //当前位置     int nextPos = 0;//下一位置         while(s < n)    {        pos = nextPos;//获取当前位置         nextPos = a[pos];//获取当前位置的下一位置        count++;        if (count == m)//改变当前位置的下一位置        {            count = 0; //计数器归零            s++; //累计出圈人             a[pos] = a[nextPos];        }       }        delete []a;        if (nextPos != 0)        return nextPos;    else         return n;}//很巧妙的方法,Fun_4()的一个改进 int Fun_5(int n, int m){   int *a = new int[n]; //设置一个数组用来存储n个人是否出圈,1表示在圈内,0表示在圈外         for (int i=n-2; i>=0; i--) //存储下一个人的编号     {        a[i] = i + 1;    }     a[n-1] = 0; //最后一个人的下一位是第一个人     int pos = n - 1; //当前位置         do    {        for (int i=0; i<m-1; i++)            pos = a[pos];          a[pos] = a[a[pos]];    } while (pos != a[pos]);        delete []a;        return pos + 1;}//很巧妙的方法,直接确定出列的人,并不断改变数组的长度 int Fun_6(int n, int m){    int *a = new int[n]; //设置一个数组用来存储n个人的编号        for (int i=0; i<n; i++) //注意C语言中的数组下标从0开始     {        a[i] = i + 1;    }         int pos = (m - 1) % n;//这是第一个要出列的人的下标         while (n > 1) // 当队列中还有不止一个人的时候,要就进行出列的动作     {        for (int i=pos; i<n-1; i++) //这个家伙走了以后,后面的人应该依次顶上来         {            a[i] = a[i+1];        }        n--;    // 并且整个队列的人少了一个,也就是长度要减掉一         pos = (pos + m - 1) % n; //这是下一个要出列的人    }    pos = a[0];    delete []a;    return pos;}//采用循环链表结构,来进行删除操作 int Fun_7(int n, int m){    struct node    {        int data;        struct node *next;    };    struct node * head, *s, *temp;    head = new struct node;//存储第一个人的序号信息     head->data = 1;    temp = head->next = head;    for (int i=2; i<=n; i++)//存储所有人的序号信息     {        s = new struct node;        s->data = i;        s->next = head;        temp->next = s;        temp = s;    }    s = head;     while (s->next != s)    {         for (int i=1; i<m; i++)//先数m-1个数         {            temp = s;            s = s->next;        }        //把数到m的人从链表中删除         temp->next = s->next;        delete s;        s = temp->next;    }     int pos = s->data; //最后一个人     delete s;    return pos;}/*数学方法:令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n],因为实际生活中编号总是从1开始,我们输出f[n]+1 递推公式f[1]=0;f=(f[i-1]+m)%i;   (i>1) */// 递归算法int F(int n, int m){    if (n == 1)       return 0;       return (F(n-1, m) + m) % n;}int Fun_8(int n, int m){         return F(n, m) + 1;}//非递归算法int Fun_9(int n, int m){    int r = 0;    for (int i = 2; i <= n; i++)        r = (r + m) % i;    return r + 1;}int Fun_10(int n, int m){    int p, i = 1;    while( i < n )    {        p = i * m;        while (p > n)            p = p - n + (p - n - 1)/(m - 1);        i++;    }    p = n * m;    while (p > n)        p = p - n + (p - n - 1)/(m - 1);            return p;}

阅读(2928) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册