题目描述:
给定两个整数n、m,输出从(1,2,…,n)中选出m个数的所有组合。
输入:
每个测试文件含有多个数据,输入两个整数n,m(0
输出对应的组合数,每个组合中的数字由小到大排列,对于每组数据的所有组合按字典序排序
/下面这个组合代码能够满足字典排序输出
#include
using namespace std;
int n, m, lst[10] = {0};
void func(int a, int b, int c)
{
if (c != 0 && c <= b - a + 1)
{
lst[m - c] = a;//
func(a + 1, b, c - 1);//这两行是分两种情况讨论,进行递归
func(a + 1, b, c);
}
else if (c == 0)
{
cout << lst[0];
for (int j = 1; j < m; ++j)
cout << ' ' << lst[j];
cout << endl;
}
}
int main()
{
while (cin >> n >> m)
func(1, n, m);
return 0;
}
/*基本思想:从1到n中选取m个数,按照分治法可以分为两种情况:
1、选取了1,那么从2到n中选取m-1个数
2、没选取1,那么从2到n中选取m个数
使用数组储存选取的数字,递归到选取0个数时即输出一组结果
需要注意一种情况,从a到b中选取c个数,如果c>b-a+1,即剩余数字个数都不够要选取的个数
就不需要进行递归了*/
下面的题目是从n个数字中选取m个,然后再对这m个数字分别进行全排列
//该全排列代码是在前面组合代码基础数修改而来,使用STL也可以满足字典顺序输出
#include
#include //iostream_iteraotr
#include //next_permutation
using namespace std;
int n, m, lst[10] = {0};//选出来的m个数字的存放位置,先对数组初始化为全0
void permutations(int lists[], int m)//相比上面的组合方法,仅增加了这个全排列函数
{
do{
copy(lists, lists+m-1, ostream_iterator<int>(cout, " "));
cout << lists[m-1];
cout << endl;
}while (next_permutation(lists, lists+m));
}
void func(int a, int b, int c)
{
if (c != 0 && c <= b - a + 1)
{
lst[m - c] = a;//
func(a + 1, b, c - 1);//这两行是分两种情况讨论,进行递归
func(a + 1, b, c);
}
else if (c == 0)
{
/*cout << lst[0];
for (int j = 1; j < m; ++j)
cout << ' ' << lst[j];
cout << endl;*/
permutations(lst, m);
}
}
int main()
{
while (cin >> n >> m)
func(1, n, m);
return 0;
}
题目描述
给定一个整数n, 输出1~n的全排列
输入
每个测试文件只有一个数据,输入一个整数n(0
输出全排列(每个排列中的数字用空格隔开),且每组排列注意字典序输出所有排列(即要先输出123才能输出132)
//STL解法:
#include
#include
#include
using namespace std;
const int Length = 8;
int n, counts = 0;
int factorial(int n);
template<class T>
void permutations(T list[], int m)//这里和常规的解法不同,没有用开始参数k
{
do{
copy(list, list+m-1, ostream_iterator<T>(cout, " "));
cout << list[m-1];
counts++;
if (counts < factorial(n))
cout << endl;
}while (next_permutation(list, list+m));
}
int main()
{
cin >> n;
int a[Length];//可以把n替换成一个较大的常量
for (int i = 0; i < n; ++i)
a[i] = i+1;
/*char a[] = "abcde";
permutations(a, strlen(a));这里稍作修改就可以实现字符串的全排列*/
permutations(a, n);
return 0;
}
int factorial(int n)//主要是为了输出格式而增加的函数
{
int result = 1;
for (int i = 1; i <= n; ++i)
{
result *= i;
}
return result;
}
发散思维(使用prev_permutation()实现字典逆序排列输出):
#include
#include
#include
using namespace std;
const int Length = 8;
int n, counts = 0;
int factorial(int n);
template<class T>
void permutations(T list[], int m)
{
do{
copy(list, list+m-1, ostream_iterator<T>(cout, " "));
cout << list[m-1];
counts++;
if (counts < factorial(n))
cout << endl;
}while (prev_permutation(list, list+m));//改动点
}
int main()
{
cin >> n;
int a[Length];
for (int i = 0; i < n; ++i)
a[i] = n - i;//改动点
permutations(a, n);
return 0;
}
int factorial(int n)
{
int result = 1;
for (int i = 1; i <= n; ++i)
{
result *= i;
}
return result;
}
//常规解法
#include
#include
#include
using namespace std;
const int Length = 8;
int n, counts = 0;
void swap(int& a, int& b);
int factorial(int n);
template<class T>
void permutations(T list[], int k, int m)
{
if (k == m)
{
copy(list, list+m-1, ostream_iterator<T>(cout, " "));
cout << list[m-1];
counts++;
if (counts < factorial(n))
cout << endl;
}
else
{
for (int i = k; i < m; ++i)//注意从0开始到m-1,共m个数字
{
sort(list + k, list + m);//在每一步交换之前排序,保证输出按字典排序
swap(list[k], list[i]);
permutations(list, k+1, m);
swap(list[k], list[i]);
}
}
}
int main()
{
cin >> n;
int a[Length];
for (int i = 0; i < n; ++i)
a[i] = i+1;
permutations(a, 0, n);
return 0;
}
void swap(int& a, int& b)
{
int temp;
temp = a;
a = b;
b = temp;
}
int factorial(int n)
{
int result = 1;
for (int i = 1; i <= n; ++i)
{
result *= i;
}
return result;
}
这部分代码是比较通用的,可以是部分数字,也可以是部分字符的组合输出
如果还需要实现全排列,直接把全排列函数加入即可
// 求从数组a[1..n]中任选m个元素的所有组合。
// a[1..n]表示候选集,n为候选集大小,n>=m>0。
// b[1..M]用来存储当前组合中的元素(这里存储的是元素下标),
// 常量M表示满足条件的一个组合中元素的个数,M=m,这两个参数仅用来输出结果。
#include
#include
const int M = 3;
using namespace std;
void combine(char a[], int n, int m, char b[])
{
for (int i = m; i <= n; i++)// 注意这里的循环范围,正向的话就会按字典顺序输出
{//从尾到头存入下标,到m=1时,已存了0~m-1的下标,开始输出前三个数字,
//然后递归回退,再递归,等到m=1时,再输出,如此循环
b[m - 1] = i - 1;//把下标存储到数组b中
if (m > 1)
combine(a, i - 1, m - 1, b);
else // m == 1, 输出一个组合
{
for (int j = 0; j < M; j++)
cout << a[b[j]] << " ";
cout << endl;
}
}
}
int main()
{
char a[] = "abcde";
char b[M];
combine(a, strlen(a), M, b);
return 0;
}
如果需要再进一步进行全排列的话
//下面是对上面方法的进一步拓展,对选出的组合再进行全排列
// 求从数组a[1..n]中任选m个元素的所有组合。
// a[1..n]表示候选集,n为候选集大小,n>=m>0。
// b[1..M]用来存储当前组合中的元素(这里存储的是元素下标),
// 常量M表示满足条件的一个组合中元素的个数,M=m,这两个参数仅用来输出结果。
#include
#include
#include
#include
#include
const int M = 3;
using namespace std;
template<class T>
void permutations(T list[], int m)//这里和常规的解法不同,没有用开始参数k
{
do{
copy(list, list+m-1, ostream_iterator<T>(cout, " "));
cout << list[m-1];
cout << endl;
}while (next_permutation(list, list+m));
}
void combine(char a[], int n, int m, char b[])
{
for (int i = m; i <= n; i++)// 注意这里的循环范围,正向的话就会按字典顺序输出
{//从尾到头存入下标,到m=1时,已存了0~m-1的下标,开始输出前三个数字,
//然后递归回退,再递归,等到m=1时,再输出,如此循环
b[m - 1] = i - 1;//把下标存储到数组b中
if (m > 1)
combine(a, i - 1, m - 1, b);
else // m == 1, 输出一个组合
{
char permu[M];//把选出的一种组合转存入另一个数组中,方便进行进一步全排列
for (int j = 0; j < M; j++)
permu[j] = a[b[j]];
permutations(permu, M);
}
}
}
int main()
{
char a[] = "abcde";
char b[M];
combine(a, strlen(a), M, b);
return 0;
}
题目描述:
给定一个包含大写英文字母的字符串S,要求你给出对S重新排列的所有不同的排列数。
如:S为ABA,则不同的排列有ABA,AAB,BAA三种。
输入:
输入一个长度不超过10的字符串S,我们确保都是大写的
输出:
输出S重新排列的所有不同排列数(包含自己本身)。
样例:
ABA
3
ABCDEFGHHA
907200
AABBCC
90
//STL解法,能够自动处理字符重复的情况;
#include
#include
#include
#include
using namespace std;
int counts = 0;
template<class T>
void permutations(T lists)
{
do {//注意被发送到输出流的数据类型是char
copy(lists.begin(), lists.end(), ostream_iterator<char>(cout, " "));
cout << endl;
counts++;
} while (next_permutation(lists.begin(), lists.end()));
}
int main()
{
string str;
getline(cin, str);
//vector a(str.begin(), str.end());如果用vector的话,可以这样初始化P697有详细的初始化方法
sort(str.begin(), str.end());
permutations(str);
cout << counts << endl;
return 0;
}
//常规解法:需要增加一个函数来处理重复问题
#include
#include
#include
#include
using namespace std;
int counts = 0;
bool HasDuplicate(char b[], int start, int i) //i为该元素下标
{
for (int k = start; k < i; k++) //判断在该元素之前有没有相同元素
if (b[k] == b[i])
return false; //如果有,则返回false
return true;
}
template<class T>
void permutations(T lists, int k, int m)
{
if (k == m)
{
cout << lists << endl;
counts++;
}
else
{
for (int i = k; i < m; ++i)
{
sort(&lists[k], &lists[m]);//每次交换之前排序,保证字典顺序输出
if (HasDuplicate(&lists[0], k, i))//去重
{
swap(lists[k], lists[i]);
permutations(lists, k + 1, m);
swap(lists[k], lists[i]);
}
}
}
}
int main()
{
string str;
getline(cin, str);
int len = str.size();
permutations(str, 0, len);
cout << counts << endl;
return 0;
}
组合算法
本程序的思路是开一个数组,其下标表示1到m个数,数组元素的值为1表示其下标
代表的数被选中,为0则没选中。
首先初始化,将数组前n个元素置1,表示第一个组合为前n个数。
然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为
“01”组合,同时将其左边的所有“1”全部移动到数组的最左端。
当第一个“1”移动到数组的m-n的位置,即n个“1”全部移动到最右端时,就得
到了最后一个组合。
例如求5中选3的组合:
1 1 1 0 0 //1,2,3
1 1 0 1 0 //1,2,4
1 0 1 1 0 //1,3,4
0 1 1 1 0 //2,3,4
1 1 0 0 1 //1,2,5
1 0 1 0 1 //1,3,5
0 1 1 0 1 //2,3,5
1 0 0 1 1 //1,4,5
0 1 0 1 1 //2,4,5
0 0 1 1 1 //3,4,5
全排列算法
从1到N,输出全排列,共N!条。
分析:用N进制的方法吧。设一个N个单元的数组,对第一个单元做加一操作,满N进
一。每加一次一就判断一下各位数组单元有无重复,有则再转回去做加一操作,没
有则说明得到了一个排列方案
例如:求1-3的全排列,共3!条
设数组初始状态为0 0 0,以下
为计算全排列的步骤:
0 0 0 +1
1 0 0 +1
2 0 0 +1
3 0 0 满3进1 ->
0 1 0 +1
1 1 0 +1
2 1 0 +1
3 1 0 满3进1 ->
0 2 0 +1
1 2 0 +1
2 2 0 +1
3 2 0 满3进1 ->
0 3 0 满3进1 ->
0 0 1 +1
1 0 1 +1
2 0 1 +1
3 0 1 满3进1 ->
0 1 1 +1
1 1 1 +1
2 1 1 +1
3 1 1 满3进1 ->
0 2 1 +1
1 2 1 +1
2 2 1 +1
3 2 1 满3进1 ->
0 3 1 满3进1 ->
0 0 2 +1
1 0 2 +1
2 0 2 +1
3 0 2 满3进1 ->
0 1 2 +1
1 1 2 +1
2 1 2 +1
3 1 2 满3进1 ->
0 2 2 +1
1 2 2 +1
2 2 2 +1
3 2 2 满3进1 ->
0 3 2 满3进1 ->
0 0 3 满3进1 ->
0 0 0
共6个YES,每一个对应一种排列。