zoukankan      html  css  js  c++  java
  • 组合问题

    原创


    排列组合问题是算法中很基础又很常用的算法,在我的几篇知识浅薄的博客中也列出了全排列的递归算法,全排列的递归算法

    复杂,还有更加优化的算法,过几天我会写新随笔列出;今天,我写了非递归的组合算法,相比于递归的组合算法有所改进,但

    是我只是借鉴了大神博客里面的思想,在此附上链接http://www.waitingfy.com/archives/1016

    代码是自己编写的,思想水平可能也不高,有错误的地方,请大家指出,赐教了。

    解题思路不难理解,用了一种被称为二进制的方法——从N个数中取M个数组合,开辟一个N个空间的数组A,A的数组下标代表了N个

    数的下标(N个数存放在另外一个大小为N的数组中),数组A的元素置1,表明选中了这个下标,反之,表示没选中。

    问题转换为在数组A中置M个1,不重不漏的找出所有组合的方法如下:

    一、先让A中元素全置0,然后将前M个元素置1(代表先选择前M个元素的下标);

    二、从A的首元素起向右搜索,当首次遇到当前元素为1,下一个元素为0时,交换两个元素;

    三、在二步中将10组合换成01组合后,将元素0前面的所有1元素(假设有T个)全都往前推,直到前T个元素全置为1。

    四、此时得到一种组合,可以输出;回到第二步,继续寻找下一种组合,直到A后面的M个元素全部置为1(此时为最后一种组合)。

    还是辣个栗子:

    从数组 { 1,2,3,4,5 }选出3个元素组合;

    数组A的变化为:(蓝色代表10组合的交换,红色代表1的前置)

    1 1 1 0 0  —— 7 (我们按权位从左往右计算换成十进制

    1 1 0 1 0  —— 11

    1 0 1 1 0  —— 13

    0 1 1 1 0  —— 14

    1 1 0 0 1  —— 19

    1 0 1 0 1  —— 21

    0 1 1 0 1  —— 22

    1 0 0 1 1  —— 25

    0 1 0 1 1  —— 26

    0 0 1 1 1  —— 28

    按照这种方法,发现换成十进制后的数据从小到大排列(当然,也可以反过来——先将A后面的M个元素置1,接下来的步骤反向操作即可)。

    从上可知,这种方法从小到大不重不漏的找出了所有的组合数,首步的前M个元素置1即找出了最小组合数,接下来将10组合换成01组合,并且

    将0元素前的1置前是为了求次小组合数,通过从左到右的权位不难理解;在次小组合的基础上我们再求次次小的组合数。

    写的代码可能不够简洁、简练,主要是提供给大家一个思路:

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #define m 3
     4 
     5 int arr[]={2,5,64,10,55,89};
     6 
     7 void combination(int *p,int M)
     8 {
     9     int i;
    10     for(i=0;i<=sizeof(arr)/4-1;i++)
    11     {
    12         if(p[i]==1 && p[i+1]==0)    //找到'10'组合,交换 
    13         {
    14             p[i]=0;
    15             p[i+1]=1;
    16             /*
    17             int j;
    18             int count=0;    //统计交换后0前面有多少个1 
    19             for(j=0;j<i;j++)
    20             {
    21                 if( p[j]==1 )
    22                     count++;
    23             }
    24             for(j=0;j<i;j++)
    25                 p[j]=0; 
    26             for(j=0;j<count;j++)    //把1前置 
    27                 p[j]=1;
    28             */
    29             int left,right;
    30             for(left=0;left<i;left++)    // left代表0,right代表1 
    31             {
    32                 if( p[left]==0 )
    33                 {
    34                     for(right=left+1;right<i;right++)
    35                     {
    36                         if( p[right]==1 )
    37                         {
    38                             p[left]=1;    //交换 
    39                             p[right]=0;
    40                             break;
    41                         }
    42                     }
    43                 }
    44             }
    45             int j;
    46             for(j=0;j<=sizeof(arr)/4-1;j++)
    47                 if(p[j]==1)
    48                     printf("%d ",arr[j]);
    49             printf("
    ");
    50             int flag=0;
    51             for(j=sizeof(arr)/4-1;j>=sizeof(arr)/4-M;j--)
    52             {
    53                 if(p[j]==0)
    54                     flag=1;
    55             }    
    56             if(flag==0)
    57                 break; 
    58             i=-1;    //新的一轮 
    59         }
    60     }
    61 }
    62 
    63 int main()
    64 {
    65     int i;
    66     int *p=(int *)malloc(sizeof(int)*(sizeof(arr)/4));    
    67     for(i=0;i<=sizeof(arr)/4-1;i++)    //置0 
    68         p[i]=0;
    69     
    70     for(i=0;i<=m-1;i++)    //前M位置1
    71         p[i]=1;
    72     int j;
    73     for(j=0;j<=sizeof(arr)/4-1;j++)
    74         if(p[j]==1)
    75             printf("%d ",arr[j]);
    76     printf("
    ");    
    77     combination(p,m);
    78     free(p);
    79     return 0;
    80 } 

    2018-04-08

  • 相关阅读:
    Ubuntu上VNC 配置
    Ubuntu远程桌面xrdp方法
    sudo 免密码
    Ubuntu 12.04 root默认密码? 如何使用root登录?
    DNS 和 IPv6 配置攻略
    计算机专业学习浅谈
    [图像]张正友论文翻译(2)
    [图像]张正友论文翻译(1)
    [图像]用Matlab在图像上画矩形框
    word如何修改尾注
  • 原文地址:https://www.cnblogs.com/chiweiming/p/8735527.html
Copyright © 2011-2022 走看看