• 约瑟夫环


    约瑟夫问题:

    N个人围成一圈,第一个人从1开始报数,报M的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的胜利者。

    思路一:

    数组模拟

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <iostream>
     4 #include <string>
     5 #include <math.h>
     6 #include <algorithm>
     7 #include <vector>
     8 #include <stack>
     9 #include <queue>
    10 #include <set>
    11 #include <map>
    12 #include <sstream>
    13 const int INF=0x3f3f3f3f;
    14 typedef long long LL;
    15 const int mod=1e9+7;
    16 //const double PI=acos(-1);
    17 #define Bug cout<<"---------------------"<<endl
    18 const int maxn=1e5+10;
    19 using namespace std;
    20 
    21 int A[maxn];
    22 
    23 int main()
    24 {
    25     int n,m;
    26     while(~scanf("%d %d",&n,&m))
    27     {
    28         memset(A,0,sizeof(A));
    29         int num=n;
    30         int Index=0;
    31         int cnt=0;
    32         while(num>=1)
    33         {
    34             if(A[Index]==0)
    35             {
    36                 cnt++;
    37                 if(num==1)
    38                 {
    39                     printf("%d
    ",Index);
    40                     break;
    41                 }
    42             }
    43             if(cnt==m)
    44             {
    45                 num--;
    46                 cnt=0;
    47                 A[Index]=1;
    48             }
    49             Index++;
    50             if(Index==n)
    51                 Index=0;
    52         }
    53     }
    54     return 0;
    55 }

    思路二:

    循环链表

    下方程序问题:出队时节点并没真正使出队节点释放 

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <iostream>
     4 #include <string>
     5 #include <math.h>
     6 #include <algorithm>
     7 #include <vector>
     8 #include <stack>
     9 #include <queue>
    10 #include <set>
    11 #include <map>
    12 #include <math.h>
    13 const int INF=0x3f3f3f3f;
    14 typedef long long LL;
    15 const int mod=1e9+7;
    16 const int maxn=1e6+10;
    17 const int maxm=1e4+10;
    18 using namespace std;
    19 
    20 typedef struct node
    21 {
    22     int pos;//位置 
    23     int val;//密码 
    24     int flag;//是否出列的标志 
    25     node* next;
    26 }people,*PN;
    27 
    28 int main()
    29 {
    30     int n,m;
    31     printf("请输入n和m的值:");
    32     scanf("%d %d",&n,&m);
    33     PN first,p;
    34     printf("请依次输入各密码:");
    35     for(int i=1;i<=n;i++)
    36     {
    37         PN q=(PN)malloc(sizeof(node));
    38         scanf("%d",&q->val);
    39         q->pos=i;
    40         q->flag=0;
    41         q->next=NULL;
    42         if(i==1)
    43             first=q;
    44         else
    45             p->next=q;
    46         p=q;
    47     }
    48     p->next=first;
    49     printf("出列顺序为:
    ");
    50     p=first;
    51     int cot=0;//计数器 
    52     int num=n;//人数 
    53     while(num)
    54     {
    55         if(p->flag==0)
    56             cot++;
    57         if(cot==m)
    58         {
    59             printf("%d ",p->pos);
    60             m=p->val;//重新对m赋值 
    61             p->flag=1;
    62             cot=0;
    63             num--;
    64         }
    65         p=p->next;    
    66     }
    67     return 0;
    68 }

    思路三:

    用队列实现

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 int main(){
     5     int n,m;
     6     cin>>n>>m;
     7     queue<int> q;
     8     for(int i=1;i<=n;i++){
     9         q.push(i);
    10     }
    11     int cnt=0;
    12     while(q.size()>1){
    13         cnt++;
    14         if(cnt!=m){
    15             q.push(q.front());
    16         }
    17         else{
    18             cnt=0;
    19         }
    20         q.pop();
    21     }
    22     cout<<q.front()<<endl;
    23 }

    思路四:

    公式法

    参考:https://blog.csdn.net/u011500062/article/details/72855826

    递推公式:
    f(N,M)=(f(N−1,M)+M)%N

    f(N,M)表示,N个人报数,每报到M时杀掉那个人,最终胜利者的编号
    f(N−1,M)表示,N-1个人报数,每报到M时杀掉那个人,最终胜利者的编号

    一共11个人,他们先排成一排,假设每报到3的人被杀掉

    • 刚开始时,头一个人编号是1,从他开始报数,第一轮被杀掉的是编号3的人。
    • 编号4的人从1开始重新报数,这时候我们可以认为编号4这个人是队伍的头。第二轮被杀掉的是编号6的人。
    • 编号7的人开始重新报数,这时候我们可以认为编号7这个人是队伍的头。第三轮被杀掉的是编号9的人。
    • ……
    • 第九轮时,编号2的人开始重新报数,这时候我们可以认为编号2这个人是队伍的头。这轮被杀掉的是编号8的人。
    • 下一个人还是编号为2的人,他从1开始报数,不幸的是他在这轮被杀掉了。
    • 最后的胜利者是编号为7的人。

    下图表示这一过程(先忽视绿色的一行)

    现在再来看我们递推公式是怎么得到的!
    将上面表格的每一行看成数组,这个公式描述的是:幸存者在这一轮的下标位置

    f(1,3) :只有1个人了,那个人就是获胜者,他的下标位置是0
    f(2,3)=(f(1,3)+3)%2=3%2=1:在有2个人的时候,胜利者的下标位置为1
    f(3,3)=(f(2,3)+3)%3=4%3=1:在有3个人的时候,胜利者的下标位置为1
    f(4,3)=(f(3,3)+3)%4=4%4=0:在有4个人的时候,胜利者的下标位置为0
    ……
    f(11,3)=6 

    人数为N,报到M时,把那个人杀掉,那么数组是怎么移动的?
    每杀掉一个人,下一个人成为头,相当于把数组向前移动M位。若已知N-1个人时,胜利者的下标位置位f(N−1,M) ,则N个人的时候,就是往后移动M位,(因为有可能数组越界,超过的部分会被接到头上,所以还要模N),既f(N,M)=(f(N−1,M)+M)%n

    理解这个递推式的核心在于关注胜利者的下标位置是怎么变的。每杀掉一个人,其实就是把这个数组向前移动了M位。然后逆过来,就可以得到这个递推式。

    注意:该方法求出的结果是数组中的下标,最终的编号还要加1

    代码如下:

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <iostream>
     4 #include <string>
     5 #include <math.h>
     6 #include <algorithm>
     7 #include <vector>
     8 #include <stack>
     9 #include <queue>
    10 #include <set>
    11 #include <map>
    12 #include <sstream>
    13 const int INF=0x3f3f3f3f;
    14 typedef long long LL;
    15 const int mod=1e9+7;
    16 //const double PI=acos(-1);
    17 #define Bug cout<<"---------------------"<<endl
    18 const int maxn=1e5+10;
    19 using namespace std;
    20 
    21 int Solve(int n,int m)
    22 {
    23     int t=0;//i=1时,胜利者的下标位置为0
    24     for(int i=2;i<=n;i++)//递推
    25         t=(t+m)%i;
    26     return t;
    27 }
    28 
    29 int main()
    30 {
    31     int n,m;
    32     while(~scanf("%d %d",&n,&m))
    33     {
    34         printf("%d
    ",Solve(n,m));
    35     }
    36     return 0;
    37 }

    下文摘自:https://blog.csdn.net/qq_23502651/article/details/89919817

    typedef long long ll;
    ll calc(int n, ll m) {
        ll p = 0;
        for (int i = 2; i <= n; i++) {
            p = (p + m) % i;
        }
        return p + 1;//
    }

    ll cal1(ll n, ll m, ll k) { // (k == n)equal(calc)
        ll p = m % (n - k + 1);
        if (p == 0) p = n - k + 1;
        for (ll i = 2; i <= k; i++) {
            p = (p + m - 1) % (n - k + i) + 1;
        }
        return p;
    }

    递归:

    int ysfh(int n,int m,int k)//n个人报m,第k个出列的编号,追忆编号要+1 
    {
        if(k==1) return (m-1+n)%n;
        else return (ysfh(n-1,m,k-1)+m)%n; 
    }

    ll cal2(ll n, ll m, ll k) {
        if (m == 1) return k;
        else {
            ll a = n - k + 1, b = 1;
            ll c = m % a, x = 0;
            if (c == 0) c = a;
            while (b + x <= k) {
                a += x, b += x, c += m * x;
                c %= a;
                if (c == 0) c = a;
                x = (a - c) / (m - 1) + 1;
            }
            c += (k - b) * m;
            c %= n;
            if (c == 0) c = n;
            return c;
        }
    }

    -

  • 相关阅读:
    POJ1064 Cable master(二分 浮点误差)
    Codeforces1113F. Sasha and Interesting Fact from Graph Theory(组合数学 计数 广义Cayley定理)
    Codeforces1100F. Ivan and Burgers(离线+线性基)
    Codeforces1097D. Makoto and a Blackboard(数论+dp+概率期望)
    Codeforces1099F. Cookies(线段树+dp+贪心+博弈)
    python-zx笔记4-文件操作
    python-zx笔记3-函数
    python-zx笔记2-help
    fiddler使用笔记1
    理解OAuth 2.0
  • 原文地址:https://www.cnblogs.com/jiamian/p/11722878.html
Copyright © 2020-2023  润新知