zoukankan      html  css  js  c++  java
  • 三种用递归实现的枚举形式:指数、排列和组合

    这是算法进阶上的三个例子。

    一、指数型枚举

    从 1~n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。

    输入格式

    输入一个整数n。

    输出格式

    每行输出一种方案。

    同一行内的数必须升序排列,相邻两个数用恰好1个空格隔开。

    对于没有选任何数的方案,输出空行。

    本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。

    数据范围

    1n15

      每个点有选或不选两种状态,故共有2^n种排列。直接在每次缩小问题时枚举选或不选当前数字两种状态。

    1. #include <iostream>  
    2. #include <cstdio>  
    3. #include <cstring>  
    4. #include <vector>  
    5. using namespace std;  
    6. int n;  
    7. vector<int> sel;  
    8. void cal(int x) {  
    9.     if (x == n + 1) {  
    10.         for (int i = 0; i < sel.size(); ++i)  
    11.             printf("%d ", sel[i]);  
    12.         putchar(' ');  
    13.         return;  
    14.     }  
    15.     cal(x + 1);  
    16.     sel.push_back(x);  
    17.     cal(x + 1);  
    18.     sel.pop_back();  
    19.     return;  
    20. }  
    21. int main() {  
    22.     cin >> n;  
    23.     cal(1);  
    24. }  

      当然,我们有更神奇的递推做法来枚举二进制子集。(我对它的正确性理解不能

    1. #include <iostream>  
    2. #include <cstdio>  
    3. using namespace std;  
    4. int main() {  
    5.     int n;  
    6.     cin >> n;  
    7.     int s = (1 << n) - 1;  
    8.     for (int i = s; i; i = (i - 1) & s) {  
    9.         for (int j = 0; j < n; ++j) if (i >> j & 1)  
    10.             printf("%d ", j + 1);  
    11.         putchar(' ');  
    12.     }  
    13.     putchar(' ');  
    14.     return 0;  
    15. }  

    二、递归实现组合型枚举

    从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。

    输入格式

    两个整数 n,m ,在同一行用空格隔开。

    输出格式

    按照从小到大的顺序输出所有方案,每行1个。

    首先,同一行内的数升序排列,相邻两个数用一个空格隔开。

    其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如1 3 5 7排在1 3 6 8前面)。

    数据范围

    n>0 ,
    0mn ,
    n+(nm)25

      要保证选m个,给指数型枚举加上一个剪枝就好了。如果要按字典序枚举,只要从小到大枚举数字,优先选择选当前的数递归即可。

    1. #include <cstdio>  
    2. #include <vector>  
    3. #include <iostream>  
    4. using namespace std;  
    5. int n, m;  
    6. vector<int> sel;  
    7. void cal(int x) {  
    8.     if (sel.size() > m || sel.size() + (n - x + 1) < m)  
    9.         return;  
    10.     if (x == n + 1) {  
    11.         for (int i = 0; i < sel.size(); ++i)  
    12.             printf("%d ", sel[i]);  
    13.         putchar(' ');  
    14.         return;  
    15.     }  
    16.     sel.push_back(x);  
    17.     cal(x + 1);  
    18.     sel.pop_back();  
    19.     cal(x + 1);  
    20.     return;  
    21. }  
    22. int main() {  
    23.     cin >> n >> m;  
    24.     cal(1);  
    25. }  

    (20.4.25更新)改良上述方法,无需剪枝,只要保证每次从当前数列最后一项向上枚举下一项即可。

    代码:

    1. #include <iostream>  
    2. #include <cstdio>  
    3. #include <cstring>  
    4. #include <algorithm>  
    5. using namespace std;  
    6. int a[11], n, m;  
    7. void cal(int k) {  
    8.     if (k == m + 1) {  
    9.         for (int i = 1; i <= n; ++i)  
    10.             printf("%d ", a[i]);  
    11.         putchar(' ');  
    12.         return;  
    13.     }  
    14.     for (int i = a[k-1]+1; i <= n; ++i) {  
    15.         a[k] = i;  
    16.         cal(k + 1);  
    17.     }  
    18.     return;  
    19. }  
    20. int main() {  
    21.     cin >> n >> m;  
    22.     cal(1);  
    23. }  

    三、递归实现排列型枚举

      枚举全排列用库函数next_permutation()即可。用递归来实现的话,我们从当前未选择的数中从小到大枚举下一个位置上的数,同样可以保证按字典序枚举。

    1. #include <iostream>  
    2. #include <cstdio>  
    3. #include <cstring>  
    4. #include <algorithm>  
    5. using namespace std;  
    6. int a[11], n;  
    7. bool vis[11];  
    8. void cal(int k) {  
    9.     if (k == n + 1) {  
    10.         for (int i = 1; i <= n; ++i)  
    11.             printf("%d ", a[i]);  
    12.         putchar(' ');  
    13.         return;  
    14.     }  
    15.     for (int i = 1; i <= n; ++i) {  
    16.         if (vis[i]) continue;  
    17.         a[k] = i;  
    18.         vis[i] = true;  
    19.         cal(k + 1);  
    20.         vis[i] = false;  
    21.     }  
    22.     return;  
    23. }  
    24. int main() {  
    25.     cin >> n;  
    26.     cal(1);  
    27. }  
    28. int mainx() {  //库函数实现
    29.     cin >> n;  
    30.     for (int i = 1; i <= n; ++i)  
    31.         a[i] = i;  
    32.     do {  
    33.         for (int i = 1; i <= n; ++i)  
    34.             printf("%d ", a[i]);  
    35.         putchar(' ');  
    36.     } while (next_permutation(a + 1, a + 1 + n));  
    37.     return 0;  
    38. }  
  • 相关阅读:
    森林 BZOJ 3123
    calc BZOJ 2655
    修路 BZOJ 4774
    无聊的计算器【数论多合一】
    矩阵乘法 BZOJ 2738
    K大数查询 BZOJ 3110
    发展城市 BZOJ 3700
    降雨量 BZOJ 1067
    chrome中showModalDialog解决方案
    MFC webbrowser读取文档的meta分析
  • 原文地址:https://www.cnblogs.com/TY02/p/11245643.html
Copyright © 2011-2022 走看看