黑马程序员技术交流社区

标题: 约瑟夫环交流 [打印本页]

作者: 魇影    时间: 2014-6-27 16:15
标题: 约瑟夫环交流


  1. #include <stdio.h>

  2. int main()
  3. {
  4. int p[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};    //代表15个
  5. int a = 0;                                              //a和b用来当作两个标记,用来指出哪个人应该退出去
  6. int b = 0;
  7. int i = 0;                                              //i 用来计数退出去几个人,控制循环结束的
  8.    
  9.     while (i!=14)                                           //i 等于14之前一直循环,即退出去14个人的话循环结束
  10.     {
  11.        if (p[a]==0){<span style="line-height: 1.5;"> </span><span style="line-height: 1.5;">//如果a角标对应的位置为0(这个位置没有人),就不会有人报数,a和b继续往后</span>
复制代码
基础考试中遇到的问题:耶稣有15个门徒,其中有一个就是出卖耶稣的叛徒,请用排除法找出这位叛徒:15人围坐一圈,从第一个开始报号:1,2,3,1,2,3……,凡是报到“3”就退出圈子,最后留在圈内的人就是出卖耶稣的叛徒,请找出它原来的序号。(C语言)
我还不会嵌套什么的,数学也不好,用最笨的方法做出来了,无聊拿出来晒一下。
自己动手,丰衣足食,没有百度,用的都是最最简单的语句,但感觉不错~:lol
另外大家可以当作无聊时练习,在这个基础思想上把它完善一下,比如如果20个人玩,数到4的人退出去。做到我们指定玩的人数,和数到几退出去,程序给出最后存活下来的人序号,欢迎交流看法和提出建议~

作者: 魇影    时间: 2014-6-27 16:17
  1. /

  2. #include <stdio.h>

  3. int main()
  4. {
  5.    
  6.     int p[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};    //代表15个人
  7.    
  8.     int a = 0;                                              //a和b用来当作两个标记,用来指出哪个人应该退出去
  9.    
  10.     int b = 0;
  11.    
  12.     int i = 0;                                              //i 用来计数退出去几个人,控制循环结束的
  13.    
  14.     while (i!=14)                                           //i 等于14之前一直循环,即退出去14个人的话循环结束
  15.     {
  16.         if (p[a]==0){         //如果a角标对应的位置为0(这个位置没有人),就不会有人报数,a和b继续往后走
  17.         a++;
  18.         b++;
  19.     };
  20.         if (a>15) {           //a走过角标15表示报数进行完一圈,把a变成0继续下一圈报数,b减少16为了保持a领先b的数字不变
  21.             a=0;
  22.             b=b-16;
  23.         };
  24.         if (!(p[a]==0))       //如果a角标对应的位置不为0(这个位置有人),这个人报一个数,再进行下面条件判断
  25.         {
  26.             if (a==b+2) {     //这个人报数后判断,若a已经领先b两个人,说明此时报数的这个人报的是3(前两个人报的数字是1和2),
  27.                 p[a]=0;       //让这个人退出去(这个角标对应的元素变成0表示),并且让b来到a位置,继续报数计数
  28.                 b=a;
  29.                 i++;          //记录退出去的人数,用来控制循环结束
  30.             }else
  31.                 a++;          //如果这个人报数后a还没有领先b两个人,说明这个人报的数字不是3,此时a加1为了领先b一个数,
  32.                               //当a领先b两个数时表示有人报到数字3了,则执行上面的操作
  33.         };
  34.         
  35.     }
  36.     for (int m=0; m<15; m++) {//最后数组中只剩下一个元素不为0
  37.         if (!(p[m]==0)) {     //找到是哪个元素不为0,打印它,表示这个位置的人到最后都没有退出圈子
  38.             printf("%d\n",p[m]);
  39.         }
  40.     }
  41.    
  42.     return 0;
  43. }
复制代码

晕,代码不知道怎么回事没贴好
作者: TLTY    时间: 2014-6-27 17:22
我基础考试最后一题也是做的这个,我的代码是:
作者: TLTY    时间: 2014-6-27 17:24
#include<stdio.h>
#define N 15
int main(){
        int a[N],n=N;
        int i,j,z,w,c=0;
        for(i=0;i<N;i++)
                a[i]=1;
        for(z=0;z<N;z++){
                for(w=0;w<N;w++)
                        if(a[w]!=0)c++;
                if(c%3==0||c==10){
                        for(i=0,j=0;i<N;i++,j++){
                                if(a[i]==0) {j--;continue;}
                                a[i]=j+1;
                                if(a[i]%3==0){
                                        a[i]=0;
                                        n--;}
                        }
                }

                else
                {
                        for(i=0,j=c%3;i<N;i++,j++){
                                if(a[i]==0) {j--;continue;}
                                        a[i]=j+1;
                                if(a[i]%3==0){
                                        a[i]=0;
                                        n--;}
                        }
                }
        if(n==1)break;
        }
        for(i=0;i<N;i++)
        {
                if(a[i]!=0)
                        printf("叛徒的原来序号是:%d\n",i);
        }
        return 0;
}
作者: 刘昭    时间: 2014-6-27 17:35
比较经典的问题,
楼主有没有背包问题的,分享一下~
作者: 完美世界    时间: 2014-6-27 17:44
我的基础题没有这个,也做了下~

#include <stdio.h>
struct Person
{
    int s;//存储这个人报出得数
    int count;//存储这个人得序号
    int k;//用作标记,当k=1时,说明这个人离开了圈子,用不再报数
}per[15]={0};
int main()
{
    int y=0;
    for (int i=0; i<15; i++)//分配每个人得序号
    {
        per[i].count=i+1;
    }
    for (int i=0,out=0; ; i++)//变量out存储离开圈子的人数
    {
        if (i==15)
        {
            i=0;
        }
        if(per[i].k!=1)//没有被标记离开的人报数
        {
            y++;
            per[i].s=y;
            if (out==14)//当离开了14个人,剩下的最后一个人
            {
                printf("这个人得序号是%d\n",per[i].count);
                break;
            }
        }
        if (per[i].s%3==0&&(per[i].k!=1))//标记离开的人
        {
            per[i].k=1;
            out++;
        }
    }
}
作者: TLTY    时间: 2014-6-27 18:16
刘昭 发表于 2014-6-27 17:35
比较经典的问题,
楼主有没有背包问题的,分享一下~

我有:
 问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。
给定两个序列A和B,称序列Z是A和B的公共子序列,是指Z同是A和B的子序列。问题要求已知两序列A和B的最长公共子序列。
三、实验代码
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#define Size 100
void LCSLength(int m,int n,char *x,char *y,int c[Size][Size],int b[Size][Size])  
{   int i,j;
   for(i=1;i<=m+1;i++) c[0]=0;
   for(i=1;i<=n+1;i++) c[0]=0;
   for(i=1;i<=m+1;i++)
    for(j=1;j<=n+1;j++)
     { if(x==y[j])
       { c[j]=c[i-1][j-1]+1; b[j]=0; }
      else if(c[i-1][j]>=c[j-1])
       { c[j]=c[i-1][j];
         b[j]=1;  }
      else { c[j]=c[j-1];
             b[j]=2; }
     }
}
void LCS(int i,int j,char *x,int b[Size][Size])   
{ if(i==0||j==0) return;
    if(b[j]==0)
      {  LCS(i-1,j-1,x,b);
         printf("%c",x); }
    else if(b[j]==1) LCS(i-1,j,x,b);
    else   LCS(i,j-1,x,b);
}
main()
{   int m,n,i;
    int c[Size][Size],b[Size][Size];
    char x[Size],y[Size];
    printf("输入序列x的长度(小于100):");
    scanf("%d",&m);
    printf("输入序列y的长度(小于100):");
    scanf("%d",&n);
    i=1;                                   
    printf("输入 x的成员(不用空格,直接输入字符串):\n");
    while(i<m+2)                           
        { scanf("%c",&x);
          if(x!='\0')   i++;
        }
    i=1;                                
    printf("输入 y的成员(不用空格,直接输入字符串):\n");
    while(i<n+2)                          
       {  scanf("%c",&y);
          if(y!='\0')   i++;
        }
    LCSLength(m,n,x,y,c,b);
        printf("最长公共子序列:\n");
    LCS(m+1,n+1,x,b);printf("\n");
}

作者: 完美世界    时间: 2014-6-27 18:39
这个问题的另一种简单的办法

int main()
{
    int p[15]={0};
    int i=0,k=0,h=0;
    while (k!=-15)
    {
        if (p[i]>=0)
        {
            h++;
            p[i]=h;
            printf("第%d个人报数%d\n",i+1,h);
        }
        if (p[i]%3==0&&p[i]>=0)
        {
            k--;
            p[i]=k;
            printf("第%d号的人离开,离开了%d个人\n",i+1,k);
        }
        i++;
        if (i==15)
        {
            i=0;
        }
    }
    printf("这个人是%d号\n",i);
   
}
作者: 邱蚓    时间: 2014-6-27 19:15
没遇到。。难道你们的基础题那么难吗
作者: fantacyleo    时间: 2014-6-27 20:26
算法设计上暂时没想到好的改进,只想到了在数据结构上做文章,没测试过,也不知道对不对:既然叫xxx环,干脆就构造一个头尾相连的环状链表,设置一个变量表示还剩几个人,然后就一直不停数下去,报到3的就删除相应节点,直到剩1个人。由于链表删除是常数时间,而从n人删到1人要的步数是对数级别(删除3的倍数,即每次删去1/3,剩余人数为原先的2/3),这样就可以把时间复杂度控制在对数级别,但代价是链表的空间开销是数组的2倍。
作者: fantacyleo    时间: 2014-6-27 20:35
更正:时间复杂度不是对数级别,而是log(n)*log(n)。用数组,每次都要走一遍长度为n的数组看某个人是否离开,时间复杂度是n*log(n),用链表可以轻松删除已离开的人,减少每轮判定次数。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2