约瑟夫环

约瑟夫问题:

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;
    }
}

-

原文地址:https://www.cnblogs.com/jiamian/p/11722878.html