洛谷 P1157 组合的输出(二进制枚举子集)

P1157
题目描述

排列与组合是常用的数学方法,其中组合就是从 nn 个元素中抽出 rr 个元素(不分顺序且 r \le n)r≤n) ,我们可以简单地将 nn 个元素理解为自然数 1,2,…,n1,2,…,n ,从中任取 rr 个数。

现要求你不用递归的方法输出所有组合。

例如 n=5,r=3n=5,r=3 ,所有组合为:

12 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 5123,124,125,134,135,145,234,235,245,345

输入输出格式

输入格式:
一行两个自然数 n , r ( 1 < n < 21 , 1 r n ) n , r ( 1 < n < 21 , 1 r n )

输出格式:
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。

注意哦!输出时,每个数字需要 33 个场宽,pascal可以这样:

输入输出样例

输入样例#1:
5 3
输出样例#1:
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

思路

因为n的大小只有21,那我们可以用二进制上的每一位来代表每一个数,那么只用从0枚举到 2 21 即可然后对每个数进行&操作,若1的个数等于r的把这个数加到结构体中,因为然后还要对结构体进行按照字典序从小到大排序即可

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <math.h>
using namespace std;
struct node
{
    int v[21];
}a[3000000];
int n,r;
bool cmp(node a,node b)
{
    for(int i=0;i<r;i++)
        if(a.v[i]!=b.v[i])
        return a.v[i]<b.v[i];
}
int main()
{
    scanf("%d%d",&n,&r);
    int num=0;
    for(int i=0;i<(1<<n);i++)
    {
        int cnt=0;
        for(int j=0;j<21;j++)
            if(i&(1<<j))
                cnt++;
        if(cnt==r)
        {
            int k=0;
            for(int j=0;j<21;j++)
                if(i&(1<<j))
            {
                a[num].v[k++]=j+1;
            }
            num++;
        }
    }
    sort(a,a+num,cmp);
    for(int i=0;i<num;i++)
    {
        for(int j=0;j<r;j++)
                printf("%3d",a[i].v[j]);
        cout<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ftx456789/article/details/81348334