最近一直在研究搜索问题,发现了一个现象:很多搜索问题本质上其实就是排列组合的问题,只不过加上了某些剪枝和限制条件。在解决诸如此类的问题一般都会用到非空循环排列、全排列、一般组合或全组合。其中不重复排列和不重复组合就是一种剪枝的方法。为了理清自己的思路,同时也为了分享的目的就将自己的想法写下来。
首先说的是非空循环排列。
#include <iostream> using namespace std; const int MAX = 100; int result[MAX]; void f(int *array, int arrayLength, int l, int n) { if(l==n) { for(int i=0; i < n; i++) cout << result[i] << ' '; cout << endl; return ; } for(int i=0; i < arrayLength; i++) { result[l] = array[i]; f(array, arrayLength, l+1, n); } } int main() { int arrayLength = 5; int array[5]={1,2,3,4,5}; int count = 3; f(array, arrayLength, 0, count); return 0; }
全排列,对一个数组进行全排列
#include <iostream> using namespace std; const int MAX = 100; int result[MAX]; bool visit[MAX]; void f(int *array, int arrayLength, int l, int n) { if(l==n) { for(int i=0; i < n; i++) cout << result[i] << ' '; cout << endl; return ; } for(int i=0; i < arrayLength; i++) { // 如果此值没有被访问过 if(!visit[i]) { visit[i] = true; result[l] = array[i]; f(array, arrayLength, l+1, n); // 递归回溯用 visit[i] = false; } } } void Init() { for(int i=0; i < MAX; i++) visit[i] = false; } int main() { int array[5]={1,2,3,4,5}; int count = 3; Init(); f(array, 5, 0, count); return 0; }
在很多时候数据可能出现相等的情况,而我们又不需要相同的数据,那么我们就可以把重复的数据数据排除,这就是不重复排列。在搜索中会当做剪枝用到。
比如:1,1,2
不重复排列之后就是:1,1,2
1,2,1
2,1,1
代码如下:
#include <iostream> using namespace std; const int MAX = 10; int number[MAX], used[MAX], result[MAX]; int n; void f(int l) { if(l==n) { for(int i=0; i < n; i++) cout << result[i] << ' '; cout << endl; return ; } for(int i=0; i < n; i++) { if(used[i]>0) { used[i]--; result[l] = number[i]; f(l+1); used[i]++; } } } void readData() { int count=0, value; cin >> n; for(int i=0; i < n; i++) { cin >> value; int j; for(j=0; j < count;j ++) { if(number[j]==value) { used[j]++; break; } } if(j==count) { used[count]=1; number[count++] = value; } } } int main() { readData(); cout << "Result is :" << endl; f(0); return 0; }
不重复排列就是对全排列做了一次剪枝,在录入数据的时候用一个数组将真正的数保存起来,并另外用一个数组把每个数出现的次数也保存起来,那么在做递归的时候,在全排列中是判断某一个数是否有没有使用过,而这里其实也是一样,如果出现的次数大于一的话,那么就递减一表明其中一个数已经出现过了。
一般组合:n个数中取m 个数求组合
#include <iostream> using namespace std; const int MAX = 10; int number[MAX], result[MAX]; int n, m; void f(int l, int p) { if(l==m) { for(int i=0; i < m; i++) cout << result[i] << ' '; cout << endl; return ; } for(int i=p; i < n; i++) { result[l] = number[i]; f(l+1, i+1); } } void readData() { cin >> n >> m; for(int i=0; i < n; i++) cin >> number[i]; } int main() { readData(); f(0,0); return 0; }
全组合:输出一个集合中所有的子集。2^n
#include <iostream> using namespace std; const int MAX = 10; int number[MAX], result[MAX]; int n; void f(int l, int p) { for(int i=0;i< l; i++) cout << result[i] <<' '; cout << endl; for(int i=p; i < n; i++) { result[l] = number[i]; f(l+1, i+1); } } void readData() { cin >> n; for(int i=0; i < n; i++) cin >> number[i]; } int main() { readData(); f(0,0); return 0; }
不重复的组合
#include <iostream> using namespace std; const int MAX =10; int number[MAX], used[MAX], result[MAX]; int n; int count=0; void f(int l,int p) { for(int i=0; i < l; i++) cout << result[i] << ' '; cout << endl; for(int i=p; i < count; i++) { if(used[i]>0) { used[i]--; result[l] = number[i]; f(l+1, i); used[i]++; } } } void readData() { int value; cin >> n; for(int i=0; i < n; i++) { cin >> value; int j; for(j=0; j < count;j ++) { if(number[j]==value) { used[j]++; break; } } if(j==count) { used[count]=1; number[count++] = value; } } } int main() { readData(); f(0,0); return 0; }