排列组合中的搜索

本质是都是树的深搜,只不过是递归的出口条件,输出形式以及递归过程的条件不一样。

1:全排列问题:

输入:3

           1 2 4       

 输出:

1 2 4

1 4 2

2 1 4

2 4 1

4 1 2

4 2 1

参考代码实现:

#include<iostream>
#include<stdio.h>
using namespace std;
#define  maxn 15
int n;
int num[maxn];//输入n个数
int used[maxn];//标记num[i]是否使用过
int rcd[maxn];//标记每个位置的数
void  fullSort(int k)//全排列函数
{
   if(k==n)
   {
     for(int i=0;i<n;i++)
     {
       printf("%d",rcd[i]);
       if(i<n-1)printf(" ");
     }
     printf("\n");
     return;
   }
   else
    {
      for(int j=0;j<n;j++)//枚举所有的数
      {
         if(!used[j])//若num[i]没有使用过,则标记为已用
         {
            used[j]=1;
            rcd[k]=num[j];//在k的位置上放上该数
            fullSort(k+1);//填写写一个位置
            used[j]=0;//回溯,清零标记
         }

      }

    }

}
int  read_data()
{
    int i;
    if(scanf("%d",&n)==EOF) return 0;
    for(i=0;i<n;i++)scanf("%d",&num[i]);
    for(i=0;i<n;i++) used[i]=0;
    return 1;
}
int main()
{
    while(read_data())fullSort(0);//从第一位置开始
    return 0;
}


2:一般组合问题:

输入:4 3

         1 2  3 4

输出:1 2 3

           1 2 4

           1 3 4

           2 3 4

参考代码实现:

/*选择组合排列 C(4,3)*/
#include<iostream>
#include<fstream>
using namespace std;
#define maxn 15
int n,m;
int rcd[maxn];
int num[maxn];
void  select_Combination(int k,int p)
{
    if(k==m)
    {
       for(int i=0;i<m;i++)//只要填满m个位置就要输出
       {
         printf("%d",rcd[i]);
         if(i<m-1) printf(" ");
       }
        printf("\n");
        return;
    }
    else
    {
      for(int j=p;j<n;j++)//无序,上一次位置是num[p-1],所以这一次位置从num[p],试探填充。不需要标记有没有用过。
      {

         rcd[k]=num[j];
         select_Combination(k+1,j+1);//填下一个位置。
      }

    }

}
int  read_data()
{
    if(scanf("%d%d",&n,&m)==EOF) return 0;
    for(int i=0;i<n;i++)scanf("%d",&num[i]);
    return 1;
}
int main()
{
    while(read_data())select_Combination(0,0);
    return 0;
}

3:n个数的集合问题(2的n次方-1个情况)

输入: 3

          1 2 3

输出:1

          1 2

          1 2 3

          1 3

          2

          2 3

          3

参考代码实现:

 

/*求n个数所有集合 */
#include<iostream>
#include<stdio.h>
#include<time.h>
using namespace std;
#define maxn 15
int n;
int rcd[maxn];
int num[maxn];
void full_Combination(int k,int p)
{
   int i;
   for(i=0;i<k;i++)//每次进入递归函数都输出。
   {
     printf("%d",rcd[i]);
     if(i<k-1) printf(" ");
   }
   if(k)printf("\n");
   for(i=p;i<n;i++)//循环从p开始,结束条件i>=n;
   {
      rcd[k]=num[i];//填写该位置的数。
      full_Combination(k+1,i+1);//下个位置
    }
}
int  read_data()
{
    if(scanf("%d",&n)==EOF) return 0;
    for(int i=0;i<n;i++)scanf("%d",&num[i]);
    return 1;
}
int main()
{
    //int start,end;
    //start=clock();
    while(read_data())full_Combination(0,0);
    //end=clock();
    //cout<<(double)(end-start)/CLOCKS_PER_SEC<<endl;
    return 0;
}

4:不重复排列

输入:3

          1 1 2

 输出:1 1 2

            1 2 1

            2 1 1

思路:如果像第一种那样,就会多出1 1 2 ,2  1 1,1 2 1的重复,思想是记录本质不同的数出现的次数,输入的过程要过滤重复的元素。把循环的长度减少到不同元素的个数。

参考代码实现:

#include<iostream>
#include<stdio.h>
using namespace std;
#define maxn 10
int n,m;
int rcd[maxn];
int used[maxn];
int num[maxn];
void norepeat_Sort(int k)
{
    if(k==n)
    {
       for(int i=0;i<n;i++)
       {
          printf("%d",rcd[i]);
          if(i<n-1)printf(" ");

       }
     printf("\n");
     return;
    }
    else
    {
      for(int j=0;j<m;j++)//枚举不同元素的个数
      {
       if(used[j]>0)//若num[i]还没用完,继续用这个数填充下个位置。
       {
        used[j]--;//可用次数减少
        rcd[k]=num[j];
        norepeat_Sort(k+1);//填写下个位置
        used[j]++;//可用次数还原
       }
      }
    }
}
int read_data()
{
    int i,j,val;
    if(scanf("%d",&n)==EOF)return 0;
    m=0;
    for(i=0;i<n;i++)
    {
        scanf("%d",&val);
        for(j=0;j<m;j++)//删选不同的元素,并记录每一元素记录的次数。
        {
          if(num[j]==val)
          {
            used[j]++;
            break;
          }
        }
        if(j==m)
        {
         num[m]=val;
         used[m++]=1;
        }
    }
 return 1;
}
int main()
{
    while(read_data())norepeat_Sort(0);
    return 0;
}

5:不重复的组合

输入:3

         1 1 3

输出:1

          1 1

          1 1 3

          1 3

          3

思路:同上(本质一样)

参考代码实现:

#include<iostream>
#include<stdio.h>
using namespace std;
#define maxn 10
int n,m;
int rcd[maxn];
int used[maxn];
int num[maxn];
void norepeat_Comb(int k,int p)
{
    for(int i=0;i<k;i++)//每次都输出
       {
          printf("%d",rcd[i]);
          if(i<k-1)printf(" ");

       }
     if(k)printf("\n");
     for(int j=p;j<m;j++)//从P开始,枚举不同元素的个数
      {
       if(used[j]>0)//若num[i]还没用完,继续用这个数填充下个位置。
       {
        used[j]--;//可用次数减少
        rcd[k]=num[j];
        norepeat_Comb(k+1,j);//这里从j开始,不是j+1;因为不止一个数、
        used[j]++;//可用次数还原
       }
      }

}
int read_data()
{
    int i,j,val;
    if(scanf("%d",&n)==EOF)return 0;
    m=0;
    for(i=0;i<n;i++)
    {
        scanf("%d",&val);
        for(j=0;j<m;j++)//删选不同的元素,并记录每一元素记录的次数。
        {
          if(num[j]==val)
          {
            used[j]++;
            break;
          }
        }
        if(j==m)
        {
         num[m]=val;
         used[m++]=1;
        }
    }
 return 1;
}
int main()
{
    while(read_data())norepeat_Comb(0,0);
    return 0;
}


 

 


 

           

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值