zoukankan      html  css  js  c++  java
  • 约瑟夫问题--五种变式

    简单的约瑟夫问题:使用数组,模拟一个圈,每次数到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 }
  • 相关阅读:
    canvas背景粒子动态变化动画
    点击屏幕弹出心形效果
    前端图片的性能优化
    vue的computed和method的区别
    es6的...
    命名路由和命名视图
    编程式路由
    [思维]蚂蚁感冒
    [模板]前缀树 / 字典树及应用
    [模板]三分搜索
  • 原文地址:https://www.cnblogs.com/very-beginning/p/11991693.html
Copyright © 2011-2022 走看看