排列组合n选m,组合算法——0-1转换算法(巧妙算法)C++实现

知识储备

1.排列

        排列的定义:从n个不同元素中,任取m(m≤n,m与n均为自然数,下同)个元素按照一定的顺序排成一列,叫做从n个不同元素中取出m个元素的一个排列;从n个不同元素中取出m(m≤n)个元素的所有排列的个数,叫做从n个不同元素中取出m个元素的排列数,用符号 A(n,m)表示

                   计算公式:

注意:m中取n个数,按照一定顺序排列出来,排列是有顺序的,就算已经出现过一次的几个数。只要顺序不同,就能得出一个排列的组合,例如1,2,3和1,3,2是两个组合。

2.组合

组合的定义:从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合;从n个不同元素中取出m(m≤n)个元素的所有组合的个数,叫做从n个不同元素中取出m个元素的组合数。用符号 C(n,m) 表示。

                    计算公式:

注意:m中取n个数,将他们组合在一起,并且顺序不用管,1,2,3和1,3,2其实是一个组合。只要组合里面数不同即可

组合算法

0-1转换组合算法摘自:http://blog.chinaunix.net/uid-20684578-id-1572099.html

本算法的思路是开两个数组,一个index[n]数组,其下标0~n-1表示1到n个数,1代表的数被选中,为0则没选中。value[n]数组表示组合的数值,作为输出之用。     

  • 首先初始化,将index数组前m个元素置1,表示第一个组合为前m个数,后面的置为0。     
  • 然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为 “01”组合,同时将其左边的所有“1”全部移动到数组的最左端。一起得到下一个组合(是一起得出,是一起得出,是一起得出
  • 重复1、2步骤,当第一个“1”移动到数组的n-m的位置,即m个“1”全部移动到最右端时;即直到无法找到”10”组合,就得到了最后一个组合。

    组合的个数为:

                               

例如求5中选3的组合:

1 1 1 0 0 //1,2,3 
1 1 0 1 0 //1,2,4 
1 0 1 1 0 //1,3,4 
0 1 1 1 0 //2,3,4 
1 1 0 0 1 //1,2,5 
1 0 1 0 1 //1,3,5 
0 1 1 0 1 //2,3,5 
1 0 0 1 1 //1,4,5 
0 1 0 1 1 //2,4,5 
0 0 1 1 1 //3,4,5

代码如下:

#include <iostream>
using namespace std;
void Show(int ,int index[],int value[]);
bool judge(int,int ,int index[]);
void change(int ,int ,int index[],int value[]);
int main()
{
    int i,n,m;
    cout<<"请输入元素个数:";
    cin>>n;
    cout<<"请输入选多少元素:";
    cin>>m;
    int index[n]={0},value[n]; //index务必初始化为0,不然无法知道m个数之后里面是真还是假
    for(i=0;i<n;i++)
    {
        value[i]=i+1;//此处是赋初值,以1,2,3,4,5为例,当然任何数字都可以
    }
    change(n,m,index,value);
    return 0;
}

void Show(int n,int index[],int value[])
{
    int i;
    for(i=0;i<n;i++)
    {
        if(index[i])  cout<<value[i]<<" ";
    }
    cout<<endl;
}

bool judge(int n,int m,int index[])
{
    int i;
    for(i=n-1;i>=n-m;i--)
    {
        if(!index[i])  return false;
    }
    return true;
}

void change(int n,int m,int index[],int value[])  //核心算法函数
{
    int i,j,num=0;
    for(i=0;i<m;i++)
    {
        index[i]=1;
    }
    Show(n,index,value); //第一个组合
    num +=1;
    while(!judge(n,m,index))  //只要没使1全部移到右边,就继续循环
    {
        for(i=0;i<n-1;i++)  //注意是n-1,因为i=n-1时,i+1是不存在的
        {
            //找到10,变成01
            if(index[i]==1&&index[i+1]==0)
            {
                index[i]=0;
                index[i+1]=1;
                //将01组合左边的1全部放在数组最左边
                int count=0;
                for(j=0;j<i;j++)
                {
                    if(index[j])
                    {
                        index[j]=0;
                        index[count++]=1;
                    }
                }
                Show(n,index,value);  //输出
                num +=1;
                break;
            }
        }
    }
    cout<<"共有"<<num<<"种"<<endl;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值