DFS入门:全排列算法及POJ 1564 Sum it up详解
来源:互联网 发布:sql语句删除列 编辑:程序博客网 时间:2024/05/17 23:12
全排列算法是DFS(深度优先搜索)的一个简单入门应用。
假设我们现在给过一个数n, 要求我们输出从1到 n的n个数的所有排列方式。
详细要求可参考codevs中 1294全排列问题。
首先给出程序的主体及各个变量的作用:
#include<stdio.h>#include<stdlib.h>int main(void){ int n; scanf("%d", &n); int *flag = (int *)malloc(sizeof(int) * (n + 1)); int *result = (int *)malloc(sizeof(int) * (n + 1)); int i; for(i = 1;i <= n;i++){ flag[i] = 0; } int index = 1; DFS(result, flag, index, n); return 0;}
①flag数组标志着第i位数字是否使用,当第i位数字没有使用的时候,flag[i] = 0, 当第i位数字已经被使用的时候, flag[i] = 1。
②result数组包含了要输出的结果。
③index变量代表着当前的第几层(也就是第几位数字)。
④flag数组以及result数组分配n + 1个空间是为了更直观,从数组[1]开始存放值。
接下来给出DFS函数:
void DFS(int *result, int *flag, int index, int n){ int i; if(index == n + 1){ for(i = 1;i <= n;i++){ printf("%d ", result[i]); } printf("\n"); } else{ for(i = 1;i <= n;i++){ if(flag[i] == 0){ result[index] = i; flag[i] = 1; DFS(result, flag, index + 1, n); flag[i] = 0; } } }}
①首先该函数终止的条件是index == n + 1, 因为当index == n时最后一位数字还没有放进result中,所以真正结束的条件是n + 1。
②因为index是逐层加一,所以不会出现index > n + 1的情况,即是说,index < n + 1时证明还没有构造出一个可以输出的结果。
③当我们第一次递归至可以输出结果时,因为每一个数字依次递增都是可用的,所以第一个输出的数字肯定是1 2 ….. n,而此时flag数组上所有的值都为1代表所有数字都已经使用。
④当输出这一结果后,我们再进行回溯,flag[n] = 0意味着当前数字n不使用,然后回到上一层(index == n - 1层),循环刚好到达到第n - 1层的DFS函数处, 继续数字n - 1不使用,而此时上面的循环(i <= n)迭代加一(此时数字n没有使用所以可以flag[n] == 0 为true),所以在result数组的n - 1位置上插入了n,在下一个DFS函数中第n个位置只能插入n - 1。
举个简单的例子:假设n = 5,则第一个数是12345, 而第二个是12354.
⑤每一次循环中都会优先找到比较小的数在高位所以排序从小到大。(可以通过改变1到n的顺序来改变)
⑥其实第一次循环时当不看调用DFS函数调用时可以看为是决定第一位上的数字,当决定好第一位时再从剩下的数字中决定第二位,依次递推。
接着我们看POJ 1564 Sum it up问题:
该问题要求首先输入总和total, 然后输入n个数,并打印出n个数中其中任意个数加起来等于total的加法表达式, 其中n个数中可以有重复。
该题目的思路是在全排列的基础上还要去重。
程序主体及变量解释:
#include<stdio.h>#include<stdlib.h>#define MAX 12int total, n;int flag;int result[MAX];int container[MAX];int main(void){ int i; scanf("%d%d", &total, &n); int currentSum, index, step; while(n != 0){ currentSum = 0; index = 0; step = 0; flag = 0; for(i = 0;i < n;i++){ scanf("%d", &container[i]); } printf("Sums of %d:\n", total); DFS(index, step, currentSum); if(flag == 0){ printf("NONE\n"); } scanf("%d%d", &total, &n); }}
①flag哨兵标志着是否有解,无解则输出NONE。
②result数组存放输出结果,container数组存放n个数,这里为什么不需要全排列中的flag数组呢?因为n个数是按递减的顺序输入,求和的时候只需要考虑向后小的数,不会再加之前的数(之前的数要么已经跳过或已经加上)。
③index, step可参考全排列例子,currentSum 则代表当前DFS函数之前已经增加的数的和。
接下来给出DFS函数:
void DFS(int index, int step, int currentSum){ int i; if(currentSum == total){ flag = 1; printf("%d", result[0]); for(i = 1;i < step;i++){ printf("+%d", result[i]); } printf("\n"); } else{ if(currentSum < total && index < n){ for(i = index;i < n;i++){ if(i == index || container[i] != container[i - 1]){ result[step] = container[i]; DFS(i + 1, step + 1, currentSum + container[i]); } } } }}
①首先当前DFS判断结束的条件是curretSum = total, 其次currentSum < total && index < n代表当前函数还没有结束,只要其中一个条件不满足则代表无解(即currentSum > total 或者 index >= n)。
②接着老规矩step代表当前result数组上的第几位要存放什么数,但是在循环条件中i == index为什么呢?这跟不用flag数组的原因是一样的,之前的数不用考虑,只需要跳转到第i层就可以了(也就是把第i层的数放在step的位置上),所以下一个DFS函数跳转的层数也是i + 1,而并非index + 1。
③我们还要求去重,举个例子4 6 4 3 2 2 1 1,result上0位放3,1位放1,但是1有两个就重复了。所以每到新的一层时,第一个元素肯定是可以放入step位置的,但接下来的每一个元素都要进行判断是否跟前一个元素相等,若相等,则放在同一个step上会有重复。
这是其中解决问题的一种思路,我们接下来给出另一种思路。
假设这n个数中通过currentSum中记录当前的和,那么对于每一个数我们有两种选择,加或者不加。
我们看核心的函数DFS:
void DFS(int currentSum, int index, int skip){ int i; if(index == n + 1){ return ; } if(currentSum == total){ flag = 1; for(i = 0;i < n;i++){ if(status[i]){ printf("%d", container[i]); i++; break; } } for(;i < n;i++){ if(status[i]){ printf("+%d", container[i]); } } printf("\n"); } else if(currentSum < total){ if(skip != container[index]){ status[index] = 1; DFS(currentSum + container[index], index + 1, -1); } status[index] = 0; DFS(currentSum, index + 1, container[index]); }}
①index == n + 1因为最终判定结果在n + 1层,第n层判定的是上一个结果。
②skip作为标志意味着之前的数是否有跳过,当有跳过的时候保存上一个数,检查这一个位置是否放入相同的数。
- DFS入门:全排列算法及POJ 1564 Sum it up详解
- POJ 1564 Sum It Up dfs
- poj 1564 Sum It Up -- DFS 递归
- POJ 1564 Sum It Up(DFS)
- poj 1564 dfs(Sum It Up)
- POJ 1564--Sum It Up【DFS】
- POJ 1564 Sum It Up(DFS)
- poj 1564 Sum It Up DFS
- zoj 1711 || poj 1564 Sum It Up(DFS~~~去重~)
- POJ 1564 Sum It Up——DFS
- 【DFS】poj 1564 Sum It Up(hdu 1258)
- POJ 1564 Sum It Up (DFS+剪枝)
- POJ 题目1564 Sum It Up(DFS,去重)
- POJ 1564 && HDU 1258 Sum It Up(dfs)
- Sum It Up POJ 1564 HDU 杭电1258【DFS】
- POJ 1564 Sum It Up -dfs(回溯法)
- POJ 1564 - Sum It Up
- poj 1564 Sum It Up
- Set接口
- [leetcode 205] Isomorphic Strings
- 使用bootStrap-validator
- JVM中进程的工作目录
- 类的合理设计
- DFS入门:全排列算法及POJ 1564 Sum it up详解
- 制作initrd(1):向initrd内部更新驱动模块
- 越来越觉得自己啥都不会
- 方法的声明和实现
- Java字符串String中contains与indexOf的区别
- Python基础_scrapy安装心得
- java取得当前工作目录
- 【解决方法】安装ubuntu后,ubuntu与Windows7双系统,只能进入ubuntu不能进入win7
- 单利书写步骤