循环链表的操作
利用循环链表模拟城市的网络状态,进行节点的删除操作。模拟算法
根据题目描述的“切断网络”规则,通过模拟切断过程,判断Ulm城市(编号2)是否被最后选中。循环遍历与条件判断
遍历每个可能的间隔m
,并模拟切断过程,判断是否符合条件。动态内存管理
使用malloc
和 free
来动态分配和释放内存,模拟城市节点的删除。如果对malloc函数不了解可以看这篇文章:【C语言函数】对<malloc函数>的基本理解·非常透彻!-CSDN博客查找和删除节点
根据给定的间隔,找到并删除循环链表中的节点,直到最终目标节点(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城市最后被选中。
初始化链表
通过 create
函数生成一个包含 n
个城市的循环链表。每个城市编号从2开始,形成一个环形结构,方便模拟切断操作。
删除操作
从第一个城市开始,依次按照间隔 m
切断网络。每次切断后,链表中相应的城市节点被删除,直到找到满足条件的 m
。
条件判断
在删除过程中,如果删除的节点是Ulm城市(即编号为2的城市),则说明当前的 m
值不符合要求,程序会继续尝试下一个 m
值,直到找到满足条件的最小 m
。
结果输出
当找到符合条件的 m
值时,输出该值并停止对当前测试数据的处理,进入下一个测试数据。
O(n * n)
,其中 n
是城市数量。每次模拟切断操作需要遍历链表,最多会进行 n-2
次操作。因为每个测试数据的最大 n
为149,所以该算法在此范围内能够高效运行。题目来源:University of Ulm Local Contest 1996 (ZOJ1088, POJ2244)