【C/C++】约瑟夫变形:网络拥堵解决方案(Eeny Meeny Moo) 蓝桥杯/ACM备赛

考点概览: 【算法:模拟

  1. 循环链表的操作

    利用循环链表模拟城市的网络状态,进行节点的删除操作。
  2. 模拟算法

    根据题目描述的“切断网络”规则,通过模拟切断过程,判断Ulm城市(编号2)是否被最后选中。
  3. 循环遍历与条件判断

    遍历每个可能的间隔 m,并模拟切断过程,判断是否符合条件。
  4. 动态内存管理

    使用 mallocfree 来动态分配和释放内存,模拟城市节点的删除。如果对malloc函数不了解可以看这篇文章:【C语言函数】对<malloc函数>的基本理解·非常透彻!-CSDN博客
  5. 查找和删除节点

    根据给定的间隔,找到并删除循环链表中的节点,直到最终目标节点(Ulm城市)被选中。

题目描述

你肯定经历过很多人同时使用网络、网络变得很慢很慢。为了彻底解决这个问题,Ulm大学决定采取突发事件处理方案:在网络负荷高峰期,将公平地、系统地切断某些城市的网络。德国的城市被标上1~n的序号。比如Freiburg城市的序号为1,Ulm城市的序号为2,等等。然后选择一个数m,首先切断第1个城市的网络,然后间隔m个序号,切断对应城市的网络,如果超出范围,则取模,并且忽略已经被切断网络的城市。例如,如果n=17,m=5,被切断网络的城市依次为:[1, 6, 11, 16, 5, 12, 2, 9, 17, 10, 4, 15, 14, 3, 8, 13, 7]。

对于给定的n值,求最小的m值,使得Ulm城市(2号)最后被选中切断网络。

输入描述

输入文件包含多个测试数据。每个测试数据占一行,为一个整数n,3≤n<150,代表该国城市的个数。如果n的值为0,则表示输入结束。

输出描述

对每个测试数据,输出求得的m值。

样例输入

8
9
0

样例输出

11
2

 解析答案:

#include
#include

// 定义参与者结构体,表示每个城市(节点)
typedef struct participant
{
    int a;                  // 城市编号
    struct participant* next;  // 指向下一个城市
} par;

// 创建一个循环链表,表示n个城市的网络状态,城市编号从2开始
par* create(int n)
{
    int i;
    par *head, *p, *q;
    head = (par*)malloc(sizeof(par));  // 创建头结点
    p = head;
    head->a = 2;  // 从2号城市开始
    for (i = 1; i < n-1; i++)  // 创建n-1个城市节点
    {
        q = (par*)malloc(sizeof(par));  // 为下一个城市分配内存
        q->a = i + 2;  // 城市编号为i+2
        p->next = q;  // 链接前一个城市到下一个城市
        p = q;
    }
    q->next = head;  // 将最后一个城市的next指向头结点,形成循环链表
    return head;  // 返回链表头指针
}

int main()
{
    int n, m, i, j;
    par* head, *p, *x;
    
    // 无限循环,直到输入0结束
    while(1)
    {
        scanf("%d", &n);  // 读取城市数量n
        if (n == 0)  // 如果输入0,则结束程序
            break;

        m = 2;  // 初始时m设为2

        // 遍历所有可能的m值
        while(m)
        {
            int flag = 0;  // 标记是否找到合适的m值
            head = create(n);  // 创建循环链表

            // 模拟切断网络的过程,依次删掉城市
            for (j = 0; j < n - 2; j++)  // 需要删除n-2个城市
            {
                p = head;
                for (i = 0; i < m - 2; i++)  // 找到第m-1个城市
                    p = p->next;

                x = p->next;  // 找到需要删除的城市节点
                if (x->a == 2)  // 如果当前删除的是Ulm城市(2号城市),标记失败
                {
                    flag = 1;  // 标记失败
                    m++;  // 尝试下一个m值
                    break;  // 跳出当前循环,尝试新的m值
                }

                p->next = x->next;  // 删除当前城市
                free(x);  // 释放内存
                head = p->next;  // 更新链表头
            }

            // 如果没有删除Ulm城市,输出当前的m值
            if(flag == 0)
            {
                printf("%d\n", m);  // 输出满足条件的m值
                break;  // 跳出m循环,进入下一个测试数据
            }
        }
    }
    return 0;
}

本题分析

本题基于约瑟夫环模型改编而成,如果看起来吃力,可以循序渐进,先学会约瑟夫环模型,读下面这道题即可:【C/C++】约瑟夫环|出列游戏 蓝桥杯/ACM竞赛备赛-CSDN博客

需要注意的是本题与传统约瑟夫环的区别:本题一开始默认第一个元素必须删除,若m=1时删除的节点若根据一般删除节点的循环语句则会出现错误,但是本题仁慈在第二个元素需要最后删除,但若m=1,第一个删除的肯定是第二个节点,则不需要考虑,但是换做其他题则要重点考虑该情况!

这道题的关键在于模拟一个“环形链表”的删除操作。每次切断网络的操作会删除一个城市,直到最终Ulm城市(编号2)被选中为止。我们要通过循环模拟这个过程,找到最小的 m 值,使得Ulm城市最后被选中。

步骤分析:
  1. 初始化链表
    通过 create 函数生成一个包含 n 个城市的循环链表。每个城市编号从2开始,形成一个环形结构,方便模拟切断操作。

  2. 删除操作
    从第一个城市开始,依次按照间隔 m 切断网络。每次切断后,链表中相应的城市节点被删除,直到找到满足条件的 m

  3. 条件判断
    在删除过程中,如果删除的节点是Ulm城市(即编号为2的城市),则说明当前的 m 值不符合要求,程序会继续尝试下一个 m 值,直到找到满足条件的最小 m

  4. 结果输出
    当找到符合条件的 m 值时,输出该值并停止对当前测试数据的处理,进入下一个测试数据。

算法复杂度:
  • 时间复杂度大约为 O(n * n),其中 n 是城市数量。每次模拟切断操作需要遍历链表,最多会进行 n-2 次操作。因为每个测试数据的最大 n 为149,所以该算法在此范围内能够高效运行。

题目来源:University of Ulm Local Contest 1996 (ZOJ1088, POJ2244)

你可能感兴趣的:(c语言,c++,蓝桥杯,开发语言)