约瑟夫问题:
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; } }
-