• 约瑟夫环问题


    基本问题描述:

    已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。那么我这里主要研究的是最后一个出列的人的序号要怎么确定。

    第一种:模拟

    同方法名所说,简单的数组模拟链表,就不多讲了,贴上自己的代码,复杂度O(nm)。

     1 #include<bits/stdc++.h>
     2 #define N 20010
     3 using namespace std;
     4 int n,m,i,j,l[N],r[N],now;
     5 int main()
     6 {
     7     scanf("%d%d",&n,&m);
     8     for(i=1;i<=n;i++)l[i]=i-1,r[i]=i+1;
     9     r[0]=r[n]=1,l[1]=n,now=0;
    10     for(i=n;i;i--)
    11     {
    12         for(j=m;j;j--)now=r[now];
    13         l[r[now]]=l[now],r[l[now]]=r[now];
    14     }
    15     printf("%d
    ",now);
    16     return 0;
    17 }

    第二种:找规律,用数学方法解决

    表格中的n指总人数,m指报到m的人出圈

    m 1 2 3 4 5 6
    1 1 2 3 4 5 6
    2 1 1 3 1 3 5
    3 1 2 2 1 4 1

     

     

     

    通过更多的数据(我就不一一列举了),可以发现一个规律f(x)=(f(x-1)+m-1) mod x+1,f(0)=0(f(x)指有x个人的时候)。由此可以得到一个O(n)的做法,代码如下。(当然我在最后会证明这个式子的正确性,不想看的可以不看)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,m,i,now;
     4 int main()
     5 {
     6     scanf("%d%d",&n,&m);
     7     for(i=1;i<=n;i++)now=(now+m-1)%i+1;
     8     printf("%d
    ",now);
     9     return 0;
    10 }

    第三种:在第二种方法的基础上用数学方法优化

    我们可以发现当n很大的时候,大部分f(n)=f(n-1)+m,这样就要花很多步去加m,所以可以考虑用乘法把这些步骤一步到位,而当f(n)=f(n-1)+m成立时,f(n-1)+m-1<n,所以不妨设f(n+x+1)恰好不满足f(n+x)+m,即f(n)+x*m-1<n+x,此时f(n+x+1)=(f(n)+x*m+m-1)mod(n+x+1)+1。合并同类项后,(m-1)*x<n-f(n)+1,即(m-1)*x<=n-f(n)。

    当m=1时,活下来的人一定是n

    当m>1时,x<=(n-f(n))/(m-1),当n+x>=当前总人数时,循环停止,好了说了半天,发代码,自己看。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,m,i,fi,x;
     4 int main()
     5 {
     6     scanf("%d%d",&n,&m);
     7     if(m==1)printf("%d
    ",n);
     8     else
     9     {
    10         for(i=fi=x=0;i+x<n;)
    11             i+=x+1,fi=(fi+x*m+m-1)%i+1,x=(i-fi)/(m-1);
    12         printf("%d
    ",fi+(n-i)*m);
    13     }
    14     return 0;
    15 }

    最后,原理解释:

    首先,n个人里第一个报m的一定是(m-1)mod n+1这个人,这个人拿走后序列为(先把这个人的位置当作k)

                                   a   k+1,k+2,k+3,...,   n-1,   n,       1,...,k-2,k-1

    而n-1个人的序列是b       1,    2,    3,...,n-1-k,n-k,n-k+1,...,n-2,n-1

    然后一对比就发现ai=(bi+k-1) mod n+1,所以f(i)=(f(i-1)+(m-1)mod n)mod n+1,即f(i)=(f(i-1)+m-1)mod n+1

     

     

  • 相关阅读:
    android 解密工具
    android打包需要的图标
    Mac 创建软链接
    历届试题 Excel地址
    算法训练 字串统计
    最长回文子串
    算法提高 P1001【大数乘法】
    算法提高 拿糖果【埃氏筛 动态规划】
    算法训练 未名湖边的烦恼
    算法提高 合并石子【动态规划】
  • 原文地址:https://www.cnblogs.com/gaozeke/p/8379689.html
Copyright © 2020-2023  润新知