排列:
(A、B、C、D)的全排列为
1、A后面跟(B、C、D)的全排列
2、B后面跟(A、C、D)的全排列
3、C后面跟(A、B、D)的全排列
4、D后面跟(A、B、C)的全排列
void permutation(int a[], int m, int k) { if (k>m) { for (int i = 0; i <= m; i++) cout << a[i] << " "; cout << endl; } else { for (int i = k; i <= m; i++) { swap(a[k], a[i]); permutation(a, m, k + 1); swap(a[k], a[i]); } } } void perm(int *a, int n) { permutation(a, n - 1, 0); } int main() { int a[] = { 1, 2, 3, 4, 5 }; int len = sizeof(a) / sizeof(a[0]); perm(a, len); }
或者:
#include <iostream> using namespace std; template<typename T> void permutation(T array[], int begin, int end) { int i; if(begin == end){ for(i = 0; i <= end; ++i){ cout<<array[i]<<" "; } cout<<endl; return; } else { //for循环遍历该排列中第一个位置的所有可能情况 for(i = begin; i <= end; ++i) { swap(array[i], array[begin]); permutation(array, begin + 1, end); swap(array[i], array[begin]); } } } int main(int argc, char **argv) { int a[4] = {1, 2, 3, 4}; permutation(a, 0, sizeof(a) / sizeof(int) - 1); return 0; }
非递归全排列算法,即按字典序排列算法。
基本思想是:
1.对初始队列进行排序,找到所有排列中最小的一个排列Pmin。
2.找到刚刚好比Pmin大比其它都小的排列P(min+1)。
3.循环执行第二步,直到找到一个最大的排列,算法结束。
如排列ABCDE,这是所有排列中最小的一个排列,刚好比ABCDE大的排列是:ABCED。
算法如下:
给定已知序列P = A1A2A3.....An
对P按字典排序,得到P的一个最小排列Pmin = A1A2A3....An ,满足Ai > A(i-1) (1 < i <= n)
从Pmin开始,找到刚好比Pmin大的一个排列P(min+1),再找到刚好比P(min+1)大的一个排列,如此重复。
1.从后向前(即从An->A1),找到第一对为升序的相邻元素,即Ai < A(i+1)。
若找不到这样的Ai,说明已经找到最后一个全排列,可以返回了。
2.从后向前,找到第一个比Ai大的数Aj,交换Ai和Aj。
3.将排列中A(i+1)A(i+2)....An这个序列的数逆序倒置,即An.....A(i+2)A(i+1)。因为由前面第1、2可以得知,A(i+1)>=A(i+2)>=.....>=An,这为一个升序序列,应将该序列逆序倒置,所得到的新排列才刚刚好比上个排列大。
4.重复步骤1-3,直到返回。
更详细看:
全排列的非递归就是由后向前找替换数和替换点,然后由后向前找第一个比替换数大的数与替换数交换,最后颠倒替换点后的所有数据。
http://blog.csdn.net/nanjunxiao/article/details/9081487 非常好:
http://blog.csdn.net/morewindows/article/details/7370155/
这个算法是C++ STL算法next_permutation的思想。
/********************************************************************** * Compiler: GCC * Last Update: Mon 07 May 2012 07:08:58 PM CST * File Name: 1.cpp * Description: ************************************************************************/ #include <iostream> #include <cstring> using namespace std; //交换数组a中下标为i和j的两个元素的值 void swap(int *a,int i,int j) { a[i]^=a[j]; a[j]^=a[i]; a[i]^=a[j]; } //将数组a中的下标i到下标j之间的所有元素逆序倒置 void reverse(int a[],int i,int j) { for(; i<j; ++i,--j) { swap(a,i,j); } } void print(int a[],int length) { for(int i=0; i<length; ++i) cout<<a[i]<<" "; cout<<endl; } //求取全排列,打印结果 void combination(int a[],int length) { if(length<2) return; bool end=false; while(true) { print(a,length); int i,j; //找到不符合趋势的元素的下标i for(i=length-2; i>=0; --i) { if(a[i]<a[i+1]) break; else if(i==0) return; } for(j=length-1; j>i; --j) { if(a[j]>a[i]) break; } swap(a,i,j); reverse(a,i+1,length-1); } } int main(int argc, char **argv) { int a[4] = {1, 2, 3, 4}; combination(a, sizeof(a) / sizeof(int)); return 0; }
用STL实现:
STL有一个函数next_permutation(),它的作用是如果对于一个序列,存在按照字典排序后这个排列的下一个排列,那么就返回true且产生这个排列,否则返回false。
void stl_perm3(int a[], int n) { sort(a, a + n); do{ for (int i = 0; i < n; i++) cout << a[i] << " "; cout << endl; } while (next_permutation(a, a + n)); }
转自:http://blog.csdn.net/e3399/article/details/7543861
求组合数(C(n,r)
int main() { int n,r; cin>>n>>r; a[0]=r; comb(n,r); cout<<count<<endl; }
另外的:
算法说明:从n个数中选m个数,可以分解为以下两步(1)首先从n个数中选取编号最大的数,然后在剩下的n-1个数中选取m-1个数,直到从n-(m-1)个数中选取1个数为止。对于OIF计算来说,m=3。(2)从n个数中选取编号次小的一个数,继续执行第(1)步,直到当前可选编号最大的数为m。下面该递归算法的C++实现,在VC 6.0里编译通过。
/*求从a[n]中选取m个数的可能组合。数组a[n]表示候选集,即影像的波段数;b[m]用来存储当前组合中的某个元素,这里存储的是这个元素在a[]中的下标;常量M表示满足条件的一个组合中元素的个数,M=m,M只用来输出结果*/
#include <iostream.h>
void Combine(int a[], int n ,int m, int b[], const int M);
int main()
{
//从N个数中选M个数,N=6,M=3
const int N = 6;
const int M = 3;
int a[N];
int b[M]; //用来存储当前组合中元素(这里存储是元素下标)
for (int i = 0; i < N; i++)
a[i] = i+1;
Combine(a, N, M, b, M);
return 0;
}
void Combine(int a[], int n ,int m, int b[], const int M)
{
for (int i = n; i >= m; i--)
{
b[m-1] = i -1; //选取候选集a[]中最大的一个数,记录其下标
if (m > 1)
Combine(a, i-1, m-1, b, M); //从n-1中再选m-1个元素
else //1==m,没有元素可选,一个组合已经完成,输出
{
for (int j = M-1; j >= 0; j--)
cout<<a[b[j]];
cout<<endl;
}
}
}
回溯法求解:
采用回溯法找问题的解,将找到的组合以从小到大顺序存于a[0],a[1],…,a[r-1]
中,组合的元素满足以下性质:
(1) a[i+1]>a[i],后一个数字比前一个大;
(2) a[i]-i<=n-r+1。
算法具体实现如下(以求5个自然数中3个数的组合):
如图所示:为一个求组合问题的解空间,以深度优先的方式对该树进行遍历,到叶节点是输出一个解,当整棵树遍历完成之后就可以得到问题的所有解:
算法的具体实现如下:、
#include <stdio.h> #include <stdlib.h> #include <malloc.h> int n;//自然数的个数 int r; int *com;//存放一个生成的组合用于输出 void backtrack(int k); void output(); int main(int argc,char **argv) { printf("请输入自然数的个数n和组合个数r "); scanf("%d%d",&n,&r); if (r>n) { printf("输入数据错误!"); return 0; } com=(int*)malloc(r*sizeof(int)); com[0]=1;//组合数是从1开始的 backtrack(0); return 1; } void backtrack(int k) { int i=0; int j=0; if(k>=r) { output();//到达叶节点输出结果 return; }else{ for(j=1;j<=n-com[k]+1;j++)//遍历一个节点下的所有节点 { com[k+1]=com[k]+j; backtrack(k+1);//前进 ,递归 com[k]++; backtrack(k);//回溯 } } } void output() { int i=0; for (i=0;i<r;i++) { printf("%d ",com[i]); } printf(" "); }
参考:http://www.cnblogs.com/black-pearl/archive/2013/05/10/3071721.html
为什么是
for(int j=1;j<=n-comb[k]+1;j++)
不是j<=n-comb[k];
更多:http://hi.baidu.com/mafia1974/item/96f7f79ea6595d82581461bf
网上面看到的解法:
典型的排列组合问题,首选回溯法,为了简化问题,我们将a中n个元素值分别设置为1-n
#include <iostream> #include <vector> using namespace std; int array[5] = {0,1,2,3,4}; vector<int> obj; #define NULLELEMENT -1 //组合问题 void selectC(int depth) { if (depth == 3) { for (int i = 0; i < obj.size(); ++i) { cout << obj[i] << " "; } cout << endl; return; } for (int i = 0; i < 5; ++i) { if (obj.size() > 0 && i <= obj[obj.size() -1]) continue; obj.push_back(array[i]); selectC(depth+1); obj.pop_back(); } } //排列组合 void selectA(int depth) { if (depth == 3) { for (int i = 0; i < obj.size(); ++i) { cout << obj[i] << " "; } cout << endl; return; } for (int i = 0; i < 5; ++i) { if (array[i] == NULLELEMENT) continue; obj.push_back(i); array[i] = NULLELEMENT; selectA(depth+1); array[i] = i; obj.pop_back(); } } int main() { selectA(0); selectC(0); return 0; }
---------------
变态组合数C(n,m)求解
问题:求解组合数C(n,m),即从n个相同物品中取出m个的方案数,由于结果可能非常大,对结果模10007即可。