标准的C(n,m)求解
#if 0
// 对于求C(n, m),从第一个字符开始扫描,每个字符有两种情况,要么被选中,要么不被选中,如果被选中,递归求解C(n-1, m-1)。
// 如果未被选中,递归求解C(n-1, m)。不管哪种方式,n的值都会减少,递归的终止条件n=0或m=0。
// 数组的全组合数
void combination(vector<int> src, int i,int m, vector<int> &res, vector<vector<int>> &vecs)
{
if (i>=src.size()&&m!=0)
{
return;
}
if (m==0) //递归终止条件
{
copy(res.begin(), res.end(), ostream_iterator<int>(cout, " "));
cout << endl;
return;
}
//选择该元素
res.push_back(src[i]);
combination(src, i + 1, m - 1, res, vecs);
res.pop_back();
//不选该元素
combination(src, i+1, m, res, vecs);
return;
}
int main()
{
int m, n;
cin >> m >> n; //C(n,m)
vector<int> input;
for (int i = 0; i < n;i++)
{
input.push_back(i + 1);
}
vector<vector<int>> vecs;
vector<int> vec;
//combination(input,0, m, vec, vecs); ////C(n,m)
for (int i = 1; i <= n;i++) //C(n, 1), C(n, 2),...C(n, n)的总和
{
combination(input,0,i,vec,vecs); ////C(n,m)
}
return 0;
}
#endif
![](https://images2018.cnblogs.com/blog/864046/201803/864046-20180329143937077-249337035.png)
题目
链接:https://www.nowcoder.com/questionTerminal/dd7fe2adc9ec4da5a91da1e224a7ad55
来源:牛客网
科室素拓进行游戏,游戏规则如下:随机抽取9个人作为游戏参与人员,分别编号1至9,每轮要求k(k<=9且k>=0)个人自由组合使编号之和为n。输出满足规则的所有可能的组合。要求组合内部编号升序输出,组合之间无顺序要求。
输入描述:
输入数据为以空格分隔的两个整数k和n
输出描述:
每行输出一个可能的编号组合,组合内部各个编号以空格分隔升序输出。若无满足规则的组合,则输出None
示例1
输入
3 15
输出
1 5 9
1 6 8
2 4 9
2 5 8
2 6 7
3 4 8
3 5 7
4 5 6
解析
#include<iostream>
#include<math.h>
#include <vector>
#include <string>
#include <deque>
#include <stack>
#include <queue>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <algorithm>
#include <functional>
#include <numeric> //accmulate
#include <iterator> //ostream_iterator
#include <fstream>
#include <iomanip> //setprecision() setw()
using namespace std;
//#define cin infile //一定不能再oj系统中,有错,导致超时等!!!
//C++文件输入
ifstream infile("in.txt", ifstream::in);
#include <limits>
#define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */
#define INT_MAX 2147483647 /* maximum (signed) int value */
#if 1
bool flag = false;
void combination(vector<int> src, int i, int m, vector<int> &res, vector<vector<int>> &vecs,int target)
{
if (i >= src.size() && m != 0)
{
return;
}
if (m == 0) //递归终止条件,个数
{
if (accumulate(res.begin(), res.end(), 0) == target)
{
flag = true;
copy(res.begin(), res.end(), ostream_iterator<int>(cout, " "));
cout << endl;
}
return;
}
//选择该元素
res.push_back(src[i]);
combination(src, i + 1, m - 1, res, vecs,target);
res.pop_back();
//不选该元素
combination(src, i + 1, m, res, vecs,target);
return;
}
int main()
{
int a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int m, n;
cin >> m >> n; //C(9,m) ;sum()=n
vector<int> input(a,a+9);
vector<vector<int>> vecs;
vector<int> vec;
combination(input, 0, m, vec, vecs,n); ////C(n,m)
if (!flag)
{
cout << "None" << endl;
}
return 0;
}
#endif
#if 1
void PrintCombination(int *a, int n, int sum, vector<int>& vec)
{ //a为输入数组,n为数组长度,sum为待查找的和,vec用于保存查找到的组合
if (sum==0)
{
vector<int>::iterator iter=vec.begin();
for (;iter!=vec.end();iter++)
{
cout<<*iter<<" ";
}
cout<<endl;
return;
}
else if(sum<0 || n<=0)
return;
vec.push_back(a[0]);//a[0]即*a,注指针a是变化的,每次指向后一个
PrintCombination(a+1,n-1,sum-a[0],vec);
vec.pop_back();
while(*a == *(a+1) && a < a+n) //跳过重复的数字
a++;
PrintCombination(a+1,n-1,sum,vec);
}
void main()
{
int a[8]={8,3,6,5,7,2,4,1};
cout<<"原来的数组:";
copy(a, a + 8, ostream_iterator<int>(cout, " "));
sort(a,a+8);
cout<<"排序后数组:";
copy(a, a + 8, ostream_iterator<int>(cout, " "));
cout<<"-----------------------------"<<endl;
vector<int> vec;
int sum=10;
cout<<"和为"<<sum<<"的组合如下:"<<endl;
PrintCombination(a,8,sum,vec);
return;
}
#endif
![](https://images2018.cnblogs.com/blog/864046/201803/864046-20180329151005803-303416686.png)
问题2:求从n个数组任意选取一个元素的所有组合
求从n个数组任意选取一个元素的所有组合,对于这个问题,我们在直观上感觉很容易,但是用程序实现时则发现用for循环解决不了问题,因为n是随意的。
在这里,我们用递归的思想,对于数据[1, 3, 4]; [2, 5]; [6, 7];
问题3:打靶问题。一个射击运动员打靶,靶一共有10环,连开10 枪打中90环的可能性有多少?
- 思路:这道题的思路与字符串的组合很像,用递归解决。一次射击有11种可能,命中1环至10环,或脱靶。
- 参考代码
//函数功能 : 求解number次打中sum环的种数
//函数参数 : number为打靶次数,sum为需要命中的环数,result用来保存中间结果,total记录种数
//返回值 : 无
void ShootProblem_Solution1(int number, int sum, vector<int> &result, int *total)
{
if(sum < 0 || number * 10 < sum) //加number * 10 < sum非常重要,它可以减少大量的递归,类似剪枝操作
return;
if(number == 1) //最后一枪
{
if(sum <= 10) //如果剩余环数小于10,只要最后一枪打sum环就可以了
{
for(unsigned i = 0; i < result.size(); i++)
cout<<result[i]<<' ';
cout<<sum<<endl;
(*total)++;
return;
}
else
return;
}
for(unsigned i = 0; i <= 10; i++) //命中0-10环
{
result.push_back(i);
ShootProblem_Solution1(number-1, sum-i, result, total); //针对剩余环数递归求解
result.pop_back();
}
}