SDUT-1197 约瑟夫问题

本文通过使用链表实现了一个经典的约瑟夫问题解决方案。详细介绍了如何通过编程来模拟这一问题的过程,包括创建链表、执行游戏规则并最终找到获胜者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

约瑟夫问题

Time Limit: 1000MS  Memory Limit: 65536KB
Problem Description
n个人想玩残酷的死亡游戏,游戏规则如下: 

n个人进行编号,分别从1到n,排成一个圈,顺时针从1开始数到m,数到m的人被杀,剩下的人继续游戏,活到最后的一个人是胜利者。

请输出最后一个人的编号。
Input
输入n和m值。
Output
输出胜利者的编号。
Example Input
5 3
Example Output
4
Hint
第一轮:3被杀第二轮:1被杀第三轮:5被杀第四轮:2被杀

Author

#include <bits/stdc++.h>
using namespace std;
typedef struct node
{
   int data;
   struct node*next;
}st;
st*head,*p,*tail,*t;
int main()
{
    int n,m,i,num;
    cin>>n>>m;
    num=0;
    head=(st*)malloc(sizeof(st));
    head->next=NULL;
    head->data=1;
    tail=head;
    for(i=2;i<=n;i++)
    {
        p=(st*)malloc(sizeof(st));
        p->data=i;
        tail->next=p;
        p->next=NULL;
        tail=p;
    }
    tail->next=head;
    t=tail;
    p=head;
    while(num!=(n-1)*m)
    {
        num++;
        if(num%m==0)
        {
            t->next=p->next;
            p=t->next;
        }
        else
        {t=p;p=p->next;}
    }
    cout<<t->data<<endl;
    return 0;
}


<think>嗯,用户想解决C语言中的删数问题,具体是SDUT的实验7-2。首先,我需要明确什么是删数问题。通常删数问题是指在一个数字序列中删除k个数字,使得剩下的数字组成的新数最小。例如,给定数字178543,删除2个数字后,最小的数是1543。这个问题需要贪心算法来解决,每次删除一个使剩余数最小的数字,重复k次。 接下来,我需要参考用户提供的引用内容。引用中的代码示例多涉及选择结构、字符编码、约瑟夫环等,可能与当前问题关系不大,但可以借鉴其中的编程结构。比如,约瑟夫问题中的筛法可能涉及到循环和条件判断,但删数问题更侧重于贪心策略。 首先,用户需要的是如何实现删数问题的C语言解法。可能的步骤包括:输入一个字符串表示的数字,确定要删除的位数k,然后循环k次,每次找到第一个比后面数字大的字符并删除。例如,对于数字“178543”,第一次删除8,得到“17543”,第二次删除7,得到“1543”。 需要注意处理前导零的情况,比如输入是“200”,删除一个0后应该是“0”而不是“00”。另外,当所有数字都相同时,或者后面数字都比前面大时,可能需要删除最后几位。 接下来,编写代码的大致流程应该是:读取字符串和k值,循环删除k次,每次从左到右找到第一个可以删除的位置,然后处理剩下的字符。最后输出结果,并去除前导零。 需要验证几个测试用例,比如输入“1432219”和k=3,输出应该是“1219”。或者输入“10200”和k=1,输出“200”。同时,处理k=0的情况,以及删除后的结果为空时返回“0”。 现在,考虑如何将这个过程转化为C语言代码。可能需要使用字符数组来操作字符串,逐个字符比较。需要注意的是,删除操作可以通过移动字符来实现,或者使用栈来优化效率,但栈的实现可能稍复杂,对于初学者来说,直接使用循环可能更直观。 例如,每次遍历字符串,找到第一个i使得str[i] > str[i+1],然后删除str[i]。如果整个字符串是递增的,则删除最后一个字符。重复这个过程k次。 最后,处理前导零,输出结果。如果结果为空,则输出0。 现在,检查是否有相关的引用内容可以借鉴。引用[4]中的选择结构示例可能对条件判断有帮助,比如比较相邻数字的大小。引用[2]中的字符数组处理和循环结构也可能有用,比如遍历每个字符并进行操作。 总结步骤: 1. 输入数字字符串和k。 2. 循环k次,每次找到要删除的位置。 3. 删除该位置的字符。 4. 处理前导零。 5. 输出结果。 需要注意的是,当k等于字符串长度时,直接返回0。此外,在每次删除后,字符串长度减少,需要更新长度。 可能出现的错误包括数组越界、未处理前导零、或者删除次数未正确完成。例如,如果k次未完全执行,可能导致结果不正确。 现在,编写代码的大纲: #include <stdio.h> #include <string.h> void delete_digit(char *num, int k) { int len = strlen(num); while (k > 0) { int i; for (i = 0; i < len - 1; i++) { if (num[i] > num[i+1]) { break; } } // 删除第i个字符 memmove(num + i, num + i + 1, len - i); len--; k--; } // 处理前导零 int start = 0; while (start < len && num[start] == '0') { start++; } if (start == len) { printf("0\n"); } else { printf("%s\n", num + start); } } int main() { char num[1000]; int k; scanf("%s %d", num, &k); delete_digit(num, k); return 0; } 测试这个代码是否能处理各种情况,比如: 输入:"178543" 2 → 输出:"1543" 输入:"10200" 1 → 输出:"200" 输入:"10" 2 → 输出:"0" 输入:"12345" 2 → 输出:"123" 需要注意,如果输入的k超过字符串长度,或者处理后的结果为空,要确保输出0。此外,memmove的使用是否正确,避免内存重叠问题。 可能还需要优化,例如在每次删除时找到第一个递减的位置,而不是每次都从头遍历。但这种方法的时间复杂度是O(k*n),在k较大时可能不够高效,但对于实验题目来说可能足够。 现在,用户的问题是关于SDUT的实验7-2删数问题,可能需要根据具体的题目要求调整输入输出格式。例如,题目可能要求输入是一个正整数n和k,删除k位后的最小数。 总结回答时,需要分步骤解释,并给出代码示例,同时处理前导零和边界情况。然后生成相关问题。</think>### SDUT C语言实验 删数问题解法 删数问题的核心是**贪心算法**,通过逐位选择最优解实现最小数构建。解决步骤如下: #### 1. 算法分析 每次删除**第一个比右侧数字大的字符**,若不存在则删除末尾字符。重复此操作$k$次,最终去除前导零即可[^3]。 #### 2. 代码实现 ```c #include <stdio.h> #include <string.h> void removeKDigits(char* num, int k) { int len = strlen(num); while (k > 0) { int i = 0; // 寻找第一个递减位置 while (i < len - 1 && num[i] <= num[i + 1]) i++; // 删除该字符 memmove(&num[i], &num[i + 1], len - i); len--; k--; } // 处理前导零 int start = 0; while (start < len && num[start] == '0') start++; printf("%s\n", (start == len) ? "0" : &num[start]); } int main() { char num[1000]; int k; scanf("%s%d", num, &k); removeKDigits(num, k); return 0; } ``` #### 3. 关键逻辑解释 - **贪心删除**:每轮删除第一个导致递减的字符,确保剩余数字最小。 - **前导零处理**:通过指针跳过前导零,若全为零则输出单个零。 - **复杂度**:时间复杂度为$O(k \cdot n)$,适用于小规模输入。 #### 4. 测试用例 - 输入:`178543 2` → 输出:`1543` - 输入:`10200 1` → 输出:`200` - 输入:`10 2` → 输出:`0`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值