Fork me on GitHub

组合数的题型

标准的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://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

问题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();  
    }  
}  
posted @ 2018-03-29 14:36  ranjiewen  阅读(884)  评论(0编辑  收藏  举报