上机回顾day4

模拟报数游戏(约瑟夫环问题)
问题描述:有n个人围成一圈,从1开始按顺序编号,从第一个人开始从1到k报数,报到k的人退出圈子;然后圈子缩小,从下一个人继续游戏,如此下去,直到留下最后一个人。要求定义函数实现。

输入:int类型的初始n与自由指定报数k。
输出:每个人的退出顺序编号。
优化目标:无。

算法描述:

#include <stdio.h>
#define MAXN 20

void CountOff( int n, int m, int out[] );

int main()
{
    int out[MAXN], n, m;
    int i;
    
    scanf("%d %d", &n, &m);
    CountOff( n, m, out );   
    for ( i = 0; i < n; i++ )
        printf("%d ", out[i]);
    printf("\n");
    
    return 0;
}

/* 你的代码将被嵌在这里 */
void CountOff( int n, int m, int out[] )
{
	int i,j,count=-1,flag;
	for(i=0;i<n;i++)
	out[i]=0;
	for(i=1;i<=n;i++)
	{
		j=0;
		while(j!=m)
		{
			count++;
			if(out[count%n]==0)
			j++;
		}
		out[count%n]=i;
	}
}

输入:

11 3

输出:

4 10 1 7 5 2 11 9 3 6 8 

升级版:
双向循环链表解决约瑟夫环问题
问题描述:
编号为1,2,…,n的n个人按顺时针方向围坐在一张圆桌周围,每人持有一个密码(正整数)。一
开始任选一个正整数m作为报数上限值,从第一个人开始按顺时针方向自1开始报数,报到m时停止报数,报m的那个人出列,将他的密码作为新的m值,从他顺时针方向的下一个人开始重新从1报数,数到m的那个人又出列;如此下去,直至圆桌周围的人全部出列为止。要求按出列顺序输出n个人的编号。

输入:第一行输入两个整数,依次表示人数n和初始化密码m,以空格间隔。
第二行依次输入n个整数,分别表示n个人的密码,以空格间隔。
输出:按出列次序输出每个人的编号,以空格间隔。

#include<stdio.h>
#include<stdlib.h>

typedef struct node{
    int data;   //手持密码值
    int num;    //编号
    struct node *prev;
    struct node *next;
}Jose_node;

//debug打印链表
void print_list(Jose_node *head,int n){
        Jose_node *p = head;
        while(n--){
            printf("data = %d,num = %d \n",p->data,p->num);
            p = p->next;
            getchar(); 
        }
}
//创建链表
Jose_node *Creatlist(){
    Jose_node *head = (Jose_node*)malloc(sizeof(Jose_node));
    head->data = 0;
    head->next = head;
    head->prev = head;
    return head;
}
//链表输入
int Insertlist(Jose_node *head){
    int sum,pwd;
    Jose_node *p = head;
    
    scanf("%d %d",&sum,&pwd);
    
    int n = sum;
    
    //在头后开始插入结点,所以结点 编号 为 2 
    int num = 2;

    //头的编号为1
    head->num = 1;
    //插入结点
    while(sum--){
        Jose_node *q = (Jose_node*)malloc(sizeof(Jose_node));
        scanf("%d",&p->data);
        q->num = num;
        num++;

        q->next = head;
        head->prev = q;
        p->next = q;
        q->prev = p;
        
        p = p->next;
    }

    //释放空结点
    p = head->prev;
    p->prev->next = p->next;
    p->next->prev = p->prev;
    free(p);
    
    //打印链表
    /* print_list(head,n); */
    
    //返回密码值
    return pwd;
}
//约瑟夫环
void Josephus(Jose_node *head,int pwd){
    int n = 1;
    Jose_node *p = NULL;
    
    while(head){
        //与密码相同
        if(n == pwd){
            printf("%d ",head->num);
            
            //结束
            if(head->next == head){
                printf("\n");
                free(head);
                break;
            }

            //重置密码
            pwd = head->data;
            
            //释放结点
            p = head; 
            head = head->next;
            p->prev->next = p->next;
            p->next->prev = p->prev;
            free(p);
            
            //从 1 开始
            n = 1;
        }else{
            //继续游戏
            head = head->next;
            n++;
        }
    }
}

int main()
{
    //创建链表
    Jose_node *head = Creatlist();
    //添加数据
    int pwd = Insertlist(head);
    //约瑟夫环
    Josephus(head,pwd);
    return 0;
}

输入:

7 20
3 1 7 2 4 8 4

输出:

6 1 4 7 2 3 5

算法流程图:
在这里插入图片描述

总结:其实约瑟夫环问题可以使用多种方法解决(还有静态链表等),不使用链表可能你们觉得要简单很多,但是当游戏变得越复杂时,利用循环链表就会让你大开眼界。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值