本质是都是树的深搜,只不过是递归的出口条件,输出形式以及递归过程的条件不一样。
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;
}