本質是都是樹的深搜,只不過是遞歸的出口條件,輸出形式以及遞歸過程的條件不一樣。
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;
}