洛谷 P1706:全排列问题 ← dfs

【题目来源】
https://www.luogu.com.cn/problem/P1706

【题目描述】
按照字典序输出自然数 1 到 n 所有不重复的排列,即 n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。

【输入格式】
​一个整数 n。

【输出格式】
由 1∼n 组成的所有不重复的数字序列,每行一个序列。
每个数字
保留 5 个场宽

【输入样例】
3

【输出样例】
    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1

【说明/提示】
1≤n≤9。

【算法分析】
● 该算法是理解回溯算法的经典案例,常用于解决排列组合相关问题。
● 题目要求保留 5 个场宽,可通过命令 
printf("%5d", x);  实现。

【算法代码】

#include <bits/stdc++.h>
using namespace std;

const int maxn=10;
int a[maxn],st[maxn];
int n;

void dfs(int step) {
    if(step==n+1) {
        for(int i=1; i<=n; i++) {
            printf("%5d",a[i]);
        }
        printf("\n");
        return;
    }

    for(int i=1; i<=n; i++) {
        if(st[i]==0) {
            a[step]=i;
            st[i]=1;
            dfs(step+1);
            st[i]=0;
        }
    }
    return;
}

int main() {
    scanf("%d",&n);
    dfs(1);
    return 0;
}




【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/119255867
https://www.luogu.com.cn/problem/solution/P1706



 

<think>好的,我现在需要帮用户解决P1157的问题,使用C语言。首先,我得先确认题目内容。可能用户没有直接给出题目,所以得先回想或者查找一下P1157的题目描述。 P1157的题目是关于组合的输出。题目要求从自然数1到n中取r个数,输出所有可能的组合,并且要按照字典序输出,每个组合中的数字用每个占3个字符的格式,使用%3d的printf格式。同时,题目中提到的例子是n=5,r=3,输出应该是前几行是1 2 3,接着1 2 4,直到最后的组合。 首先,我需要确认这个题目的具体要求。用户需要的是用C语言编写程序,正确生成并格式化输出所有组合。接下来,我得考虑如何生成这些组合。通常,生成组合可以用递归或者迭代的方法。递归可能更容易理解,但需要处理好边界条件和回溯。 接下来,我需要考虑如何存储当前生成的组合。例如,可以用一个数组来保存当前的组合,递归地填充每个位置。例如,第一个位置从1开始,第二个位置从比前一个数大的数开始,依次类推,直到填满r个数。 然后,输出格式需要注意,每个数占3个字符,用%3d,并且每个组合占一行,数字之间没有空格,但可能有三个字符的位置。比如,第一个数用%3d,后面的数也用%3d,这样中间自然会有空格填充。需要注意最后一个数后面是否需要换行。 另外,需要考虑n和r的范围,题目中虽然没有给出具体限制,但假设n不会太大,递归深度可能不会超过栈的限制。不过对于更大的n和r,可能需要非递归的方法,但这里用户可能只需要一个可行的解法。 可能的步骤: 1. 读取输入的n和r。 2. 生成所有可能的组合,按字典序排列。 3. 格式化输出每个组合,每个数占3字符,用%3d。 现在,关于生成组合的方法,递归函数可以设计为:处理当前的位置,从上一个数加1开始到某个最大值,依次填入当前位置,然后递归处理下一个位置。例如,函数参数可能包括当前处理的索引,起始数字等。 比如,定义一个数组arr来存储当前的组合,递归函数dfs(int start, int depth),其中depth表示当前处理到第几个位置。当depth等于r时,输出数组中的元素。 递归的终止条件是depth == r,此时打印数组中的元素。否则,从start到n中选择下一个数,放入arr[depth]中,然后递归调用dfs(i+1, depth+1)。 然后,主函数中读取n和r,调用dfs(1,0)或者类似的初始参数。 关于输出格式,每个数用%3d,所以在打印时,循环数组中的每个元素,用printf("%3d", arr[i]),然后在每行末尾换行。 需要注意的细节: - 初始调用时,起始数字是1,depth从0开始,直到depth等于r-1时填满数组。 - 字典序的处理是否正确,即每个位置选择的数必须比前一个位置大,确保组合是按升序排列的,这样自然满足字典序。 - 输入输出的正确性,例如测试用例n=5, r=3是否能正确输出所有组合。 可能的错误点: - 数组索引处理不正确,导致数组越界或者元素未正确填充。 - 输出格式不正确,比如每个数之间没有正确对齐,或者换行符缺失。 - 递归调用的起始值错误,导致重复的组合或者遗漏。 现在,编写代码的大致结构应该是: #include <stdio.h> int n, r; int arr[21]; // 假设n最大20 void dfs(int start, int depth) { if (depth == r) { for (int i = 0; i < r; i++) { printf("%3d", arr[i]); } printf("\n"); return; } for (int i = start; i <= n; i++) { arr[depth] = i; dfs(i + 1, depth + 1); } } int main() { scanf("%d %d", &n, &r); dfs(1, 0); return 0; } 需要验证这个代码是否正确。比如,当n=5, r=3时,是否输出正确的10个组合,并且格式正确。每个数占3字符,排列正确。 可能的问题是该递归方法是否覆盖了所有组合。比如,当depth为0时,i从1到n,然后depth为1时,i从i+1开始,这样确保了每个组合的元素是递增的,没有重复,且所有组合都被生成。 另外,题目中的例子输出,比如1 2 3,是否正确格式化为三个字符每个数,中间可能有空格填充,例如,用%3d的话,数字1会显示为两个空格加1,即“ 1”,而数字12会是“ 12”,三位数则占满。 总结来说,该问题需要使用递归生成组合,并正确格式化输出。用户可能需要这样的思路和代码,或者需要更详细的步骤解释。</think>让我们一步步解决P1157“组合的输出”问题: **题目分析** 1. 要求从1到n中选出r个数的所有组合 2. 输出必须按字典序排列 3. 每个数字用%3d格式输出(每个数字占3字符宽度) **解法思路** 1. **递归回溯法**:通过递归生成所有组合 2. **剪枝优化**:保证每个数都比前一个数大,避免重复 3. **格式化输出**:严格遵循题目要求的输出格式 **C语言实现代码** ```c #include <stdio.h> int n, r; int arr[21]; // 存储当前组合 // 递归生成组合 void dfs(int start, int depth) { // 递归终止条件:已选够r个数 if (depth == r) { for (int i = 0; i < r; i++) { printf("%3d", arr[i]); // 每个数占3字符宽度 } printf("\n"); return; } // 从start开始选择数字,保证升序 for (int i = start; i <= n; i++) { arr[depth] = i; // 选择当前数字 dfs(i + 1, depth + 1); // 递归选择下一个数字 } } int main() { scanf("%d %d", &n, &r); dfs(1, 0); // 从数字1开始,深度0启动递归 return 0; } ``` **代码解析** 1. **变量定义** - `n`:输入的上界 - `r`:需要选取的元素个数 - `arr[]`:存储当前组合的数组 2. **核心函数`dfs()`** - **参数**: - `start`:当前可选数字的起始值 - `depth`:当前递归深度(已选数字个数) - **递归过程**: 1. 当`depth == r`时输出当前组合 2. 通过循环从`start`到`n`依次选择数字 3. 递归时`i+1`保证后续数字严格递增 3. **输出格式化** - 使用`%3d`格式说明符确保每个数字占3字符宽度 - 每输出完一个组合后换行 **测试案例** 输入: ``` 5 3 ``` 输出: ``` 1 2 3 1 2 4 1 2 5 1 3 4 1 3 5 1 4 5 2 3 4 2 3 5 2 4 5 3 4 5 ``` **复杂度分析** - 时间复杂度:$O(C(n,r) \times r)$,其中$C(n,r)$是组合数 - 空间复杂度:$O(r)$(递归栈深度最大为r) **常见问题** 1. **为什么不用全排列?** - 组合不考虑顺序,排列会产生重复结果 2. **如何处理大n的情况?** - 本题n范围较小(样例n=5),递归解法足够。若n较大可改用迭代法 3. **输出格式错误怎么办?** - 注意必须使用`%3d`,不能用空格分隔,否则会判错 这个解法通过递归回溯系统地生成所有有效组合,保证了正确性和输出格式要求。理解这个基础解法后,可以进一步优化为迭代版本或处理更大数据量的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值