约瑟夫问题--五种变式
简单的约瑟夫问题:使用数组,模拟一个圈,每次数到m就标记为已读,并将num还原为0
接着从下一个人开始,如果到了最后一个人,就重新回到第一个人,继续开始寻找,如果没有被标记过num(数到的数字)++,直到所有人都退出圈子结束
源代码
1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 using namespace std;
5 int main()
6 {
7 int n,m;
8 bool vis[1000];
9 int num=0,ans=0;
10 memset(vis,0,sizeof(vis));
11 scanf ("%d%d",&n,&m);
12 for (int i= 1;i <= n+1;i++)
13 {
14 if (i==n+1)
15 i=1;
16 if (vis[i]==0)
17 num++;
18 if(num==m)
19 {
20 ans++;
21 printf ("%d ",i);
22 vis[i]=1;
23 num=0;
24 }
25 if (ans==n)
26 break;
27 }
28 return 0;
29 }
已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
例如:n = 9, k = 1, m = 5
【解答】
出局人的顺序为5, 1, 7, 4, 3, 6, 9, 2, 8。
第一种变法,从第k个人开始数,即把循环的初始值设为k即可
1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 using namespace std;
5 int main()
6 {
7 int n,m,k;
8 bool vis[1000];
9 int num=0,ans=0;
10 memset(vis,0,sizeof(vis));
11 scanf ("%d%d%d",&n,&m,&k);
12 for (int i= k;i <= n+1;i++)
13 {
14 if (i==n+1)
15 i=1;
16 if (vis[i]==0)
17 num++;
18 if(num==m)
19 {
20 ans++;
21 printf ("%d ",i);
22 vis[i]=1;
23 num=0;
24 }
25 if (ans==n)
26 break;
27 }
28 return 0;
29 }
编号为1,2,....,N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数).一开始任选一个正整数作为报数上限值M,从第一个人开始按顺时针方向自1开始顺序报数,报到M时停止报数.报M的人出列,将他的密码作为新的M值,从他在顺时针方向上的下一个人开始 重新从1报数,如此下去,直至所有人全部出列为止.试设计一个程序求出出列顺序.
测试数据:
N=7;7个人 的密码依次为:3,1,7,2,4,8,4,首先M值为6(正确的出列顺序应为6,1,4,7,2,3,5)
变式2:仍从第一个人开始数,只是每一次m都要重新更新,我们就在每次数到当前m的同时,将m更新
1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 using namespace std;
5 int main()
6 {
7 int n,m;
8 int a[1000];
9 bool vis[1000];
10 int num=0,ans=0;
11 memset(vis,0,sizeof(vis));
12 scanf ("%d%d",&n,&m);
13 for (int i = 1;i <= n;i++)
14 scanf ("%d",&a[i]);
15 for (int i= 1;i <= n+1;i++)
16 {
17 if (i==n+1)
18 i=1;
19 if (vis[i]==0)
20 num++;
21 if(num==m)
22 {
23 ans++;
24 printf ("%d ",i);
25 vis[i]=1;
26 num=0;
27 m=a[i];
28 }
29 if (ans==n)
30 break;
31 }
32 return 0;
33 }
n个人排成一圈。从某个人开始,按顺时针方向依次编号。从编号为1的人开始顺时针“一二一”报数,报到2的人退出圈子。这样不断循环下去,圈子里的人将不断减少。由于人的个数是有限的,因此最终会剩下一个人。试问最后剩下的人最开始的编号。
输入格式 Input Format
一个正整数n,表示人的个数。输入数据保证数字n不超过100。
输出格式 Output Format
一个正整数。它表示经过“一二一”报数后最后剩下的人的编号。
样例输入 Sample Input :9
样例输出 Sample Output:3
变式3:
怎么说呢,写的很复杂,首先它是1 2 1 2 1 2 1 2 报数的,题目描述的比较模糊。
先定义一个dir数组为1 2两个数字
两层循环,一个人对应一个数,如果这个人恰好在圈中而且报数为2,那么我们就可以把他踢出去
1 int dir[3]={1,2};
2 for (int i = 1;i <= n;)
3 {
4 for (int j = 0;j < 2;)
5 {
6
7 if(dir[j]==2&&vis[i]==0)
8 {
9 num++;
10 vis[i]=1;
11 if (i==n)
12 i=1;
13 else
14 i++;
15 j++;
16 continue;
17 }
18
19 if (vis[i]==1)
20 {
21 if (i==n)
22 i=1;
23 else
24 i++;
25
26 continue;
27 }
28 if (i==n)
29 i=1;
30 else
31 i++;
32 if (j==1)
33 j=0;
34 else
35 j++;
因为是一个环,所以我们可以清楚的了解到,每次如果i=n,我们就要重新从第一个人开始
如果这个人已经被访问过,我们就直接计算他的下一个人,但是应该报的数不变
完整代码....好长
1 #include <iostream>
2 #include <cstdio>
3 #include <algorithm>
4 #include <cstring>
5 using namespace std;
6 int main()
7 {
8 int n;
9 scanf ("%d",&n);
10 int num=0;
11 bool vis[1000];
12 memset(vis,0,sizeof(vis));
13 int dir[3]={1,2};
14 for (int i = 1;i <= n;)
15 {
16 for (int j = 0;j < 2;)
17 {
18
19 if(dir[j]==2&&vis[i]==0)
20 {
21 num++;
22 vis[i]=1;
23 if (i==n)
24 i=1;
25 else
26 i++;
27 j++;
28 continue;
29 }
30
31 if (vis[i]==1)
32 {
33 if (i==n)
34 i=1;
35 else
36 i++;
37
38 continue;
39 }
40 if (i==n)
41 i=1;
42 else
43 i++;
44 if (j==1)
45 j=0;
46 else
47 j++;
48 if (num==n-1)
49 {
50 for (int i = 1;i <= n;i++)
51 {
52 if (vis[i]==0)
53 cout<<i<<endl;
54 }
55 return 0;
56 }
57 }
58 }
59
60 return 0;
61 }
围绕着山顶有10个洞,狐狸要吃兔子。
兔子说:“可以,但必须找到我,我就藏身于这十个洞中,你从10号洞出发,先到1号洞找,第二次隔1个 洞找,第三次隔2个洞找,以后如此类推,次数不限。”
但狐狸从早到晚进 进出出了1000次,仍没有找到兔子。问兔子究竟藏在哪个洞里?
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #include<iostream>
5 using namespace std;
6 int b[10000+5];
7 int main()
8 {
9 int m,n,z;
10 m=1;
11 n=1;
12 z=0;
13 memset(b,0,sizeof(b));
14 for(int i=1; i<=1000; i++)
15 {
16 if(i==1)
17 {
18 b[i]=1;
19 }
20 else
21 {
22 m++;
23 if(n+m>10)
24 {
25 n=(n+m-1)%10+1;
26 b[n]=1;
27 }
28 else
29 {
30 n=n+m;
31 b[n]=1;
32 }
33 }
34 }
35 for(int i=1; i<=10; i++)
36 {
37 if(b[i]==0)
38 {
39 z++;
40 printf("%d ",i);
41 }
42 }
43 return 0;
44 }
约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。
例如N=6,M=5,被杀掉的人的序号为5,4,6,2,3。最后剩下1号。
假定在圈子里前K个为好人,后K个为坏人,你的任务是确定这样的最少M,使得所有的坏人在第一个好人之前被杀掉。
约瑟夫环变形 先引入Joseph递推公式,设有n个人(0,…,n-1),数m,则第i轮出局的人为
f(i)=(f(i-1)+m-1)%(n-i+1);
f(0)=0;
f(i) 表示当前子序列中要退出的那个人(当前序列编号为0~(n-i));
拿个例子说:K=4,M=30;
f(0)=0;
f(1)=(f(0)+30-1)%8=5; 序列(0,1,2,3,4,5,6,7)中的5
f(2)=(f(1)+30-1)%7=6; 序列(0,1,2,3,4,6,7)中的7
f(3)=(f(2)+30-1)%6=5; 序列(0,1,2,3,4,6)中的6
f(4)=(f(3)+30-1)%5=4; 序列(0,1,2,3,4)中的4
……..
依据题意,前K个退出的人必定是后K个人,所以只要前k轮中只要有一次f(i)小于k则此m不符合题意。
https://blog.csdn.net/u012717411/article/details/43916059 <--原网址
1 #include <cmath>
2 #include <climits>
3 #include <iostream>
4 using namespace std;
5 typedef long long ll;
6 const int INF=INT_MAX;
7 const int maxn = 110;
8 int k,ans[20],Joseph[20];
9 void init()
10 {
11 for(int k=1;k<=14;k++){
12 int m=1,n=2*k;
13 while(1){
14 bool ok=true;
15 ans[0]=0;
16 for(int i=1;i<=k;i++){
17 ans[i]=(ans[i-1]+m-1)%(n+1-i);
18 if(ans[i]<k) {
19 ok=false;
20 break;
21 }
22 }
23 if(ok) break;
24 m++;
25 }
26 Joseph[k]=m;
27 }
28 }
29 int main()
30 {
31 init();
32 while(cin>>k&&k){
33 cout<<Joseph[k]<<endl;
34 }
35 return 0;
36 }