算法练习:排列组合之组合和

问题描述

给出一组不同的正整数序列和一个目标值,求出所有可能的组合,使得组合里所有元素和为目标值。要求:

1)每个组合里的元素按照升序排列。

2)输出组合里不含有重复的组合。

3)输入序列中的整数可以多次使用。

举例:

输入{2,3,4,7},目标值为7

输出{7},{2,2,3},{3,4}

问题分析

为了让输出元素按升序排列,可对输入序列进行排序。同这里我们使用递归的方法来解决这个组合问题,即典型的for语句内调用递归函数。需要注意以下几点:

1)记录剩余目标值和,只有当该值为0时,组合才是有效的。

2)记录当前位置,因为序列中的数可以重复使用,所以下一次递归时,还可以从当前位置开始,这将体现在递归函数的参数里。

具体可参看代码实现中的GetResultSet函数。

扩展问题

如果序列中可能有相同的元素,并且每个元素最多只能使用一次,那么又该怎么处理?相对于之前的问题,这里有两个变化:1)每个元素最多只能使用一次,下次递归时是不能从当前位置开始的,而是从下一个开始。2)由于序列中含有相等的元素,哪怕每个元素最多只使用一次,也可能出现重复的组合,所以,为了避免重复,只取第一个相同元素。

具体可参看代码实现中的GetResultSetEx函数。

代码实现

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef vector<int> IntArray;

//结果集
typedef vector<vector<int>> ResultSet;
ResultSet gResultSet;

//原始序列中不含相同的值
void GetResultSet( const IntArray& mSrcArray, int nTarget,
				  IntArray& mDstArray, int iStart )
{
	if ( nTarget < 0 ) return;
	if ( nTarget == 0 )
	{
		//找到一个结果
		gResultSet.push_back( mDstArray );
	}
	else
	{
		for( int i = iStart; i < mSrcArray.size(); ++i )
		{
			//后面更大的数不可能满足条件
			if ( mSrcArray[i] > nTarget ) break;

			//加入当前元素
			mDstArray.push_back( mSrcArray[i] );

			//递归处理,因为元素可以重复使用,所以从当前位置继续递归
			GetResultSet( mSrcArray, nTarget-mSrcArray[i], mDstArray, i );

			//重置
			mDstArray.pop_back();
		}
	}
}

//序列中可能有相同的元素,并且每个元素最多只能使用一次,不含重复组合
void GetResultSetEx( const IntArray& mSrcArray, int nTarget,
				  IntArray& mDstArray, int iStart )
{
	if ( nTarget < 0 ) return;
	if ( nTarget == 0 )
	{
		//找到一个结果
		gResultSet.push_back( mDstArray );
	}
	else
	{
		for( int i = iStart; i < mSrcArray.size(); ++i )
		{
			//后面更大的数不可能满足条件
			if ( mSrcArray[i] > nTarget ) break;

			//避免结果集重复,只取第一个相同值加入结果中
			if ( i != iStart && mSrcArray[i] == mSrcArray[i-1] ) continue;

			//加入当前元素
			mDstArray.push_back( mSrcArray[i] );

			////递归处理,因为元素可以重复使用,所以从当前位置继续递归
			//GetResultSet( mSrcArray, nTarget-mSrcArray[i], mDstArray, i );

			//递归处理,因为元素不可以重复使用,所以从下一位置继续递归
			GetResultSetEx( mSrcArray, nTarget-mSrcArray[i], mDstArray, i+1 );

			//重置
			mDstArray.pop_back();
		}
	}
}

//输出结果集
void OutPutResultSet()
{
	if ( gResultSet.size() <= 20 )
	{
		for( ResultSet::iterator it = gResultSet.begin();
			it != gResultSet.end(); ++it )
		{
			for( IntArray::iterator itTemp = it->begin();
				itTemp != it->end(); ++itTemp )
			{
				cout << *itTemp << " ";
			}
			cout << endl;
		}

	}
	cout << "总共结果数:" << gResultSet.size() << endl;
	cout << "---------------------------------------" << endl;
}

int main()
{
	IntArray mSrcArray;
	IntArray mDstArrayTemp;
	int nTarget = 0;

	while( true )
	{
		//构造源数据
		int nTemp = 0;
		mSrcArray.clear();
		while( cin >> nTemp )
		{
			if ( nTemp == 0 ) break;
			mSrcArray.push_back( nTemp );
		}
		cin >> nTarget;

		//从小到大排序
		sort( mSrcArray.begin(), mSrcArray.end() );

		mDstArrayTemp.clear();
		gResultSet.clear();
		//GetResultSet( mSrcArray, nTarget, mDstArrayTemp, 0 );
		GetResultSetEx( mSrcArray, nTarget, mDstArrayTemp, 0 );

		//输出结果
		OutPutResultSet();
	}
	return 0;
}

系列文章说明:

1.本系列文章[算法练习],仅仅是本人学习过程的一个记录以及自我激励,没有什么说教的意思。如果能给读者带来些许知识及感悟,那是我的荣幸。

2.本系列文章是本人学习陈东锋老师《进军硅谷,程序员面试揭秘》一书而写的一些心得体会,文章大多数观点均来自此书,特此说明!

3.文章之中,难免有诸多的错误与不足,欢迎读者批评指正,谢谢.

作者:山丘儿

转载请标明出处,谢谢。原文地址:http://blog.csdn.net/s634772208/article/details/46710405

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-30 08:54:26

算法练习:排列组合之组合和的相关文章

利用标准库算法求解排列组合

以前求序列的排列时,最常用的方法就是递归回溯,现在发现其实像这样有特定算法的重复性工作是可以在STL标准库中找到答案的. 在STL的变序性算法中,有两个用于排列元素的算法分别如下: bool next_permutation(Iterator beg,Iterator end) bool prev_permutation(Iterator beg,Iterator end) 这两个算法的功能也很简单,next_permutation()会改变区间(beg,end)内的元素次序,使它们符合"下一个

算法笔记 --- 排列组合

排列组合的计算公式

算法联系:排列组合

1. Matrix.java package net.wuhx.main; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class Matrix { private String rowText = ""; private int row = 0; private int col = 0; private Point[]

C#的排列组合类

C#的排列组合类 //-----------------------------------------------------------------------------//// 算法:排列组合类//// 版权所有(C) Snowdust// 个人博客    http://blog.csdn.net/snowdust & http://snowdust.cnblogs.com// MSN & Email [email protected]//// 此源代码可免费用于各类软件(含商业软

【AtCoder】AGC005 F - Many Easy Problems 排列组合+NTT

[题目]F - Many Easy Problems [题意]给定n个点的树,定义S为大小为k的点集,则f(S)为最小的包含点集S的连通块大小,求k=1~n时的所有点集f(S)的和取模924844033.n<=2*10^5. [算法]排列组合+NTT [题解]考虑每个点只会在k个点都在其一个子树时无贡献,即: $$ANS_k=\sum_{x=1}^{n}\binom{n}{k}-\sum_{y}\binom{sz[y]}{k}+\binom{n-sz[y]}{k}$$ 令$cnt_i$表示满足s

STL_算法(17)_排列组合 next_permutation() perv_permutation()

next_permutation() prev_permutation() #include<iostream> #include<algorithm> #include<vector> // 排列组合开始之前一定要先排序 using namespace std; int main() { vector<int> ivec; ivec.push_back(1); ivec.push_back(2); ivec.push_back(3); for (vecto

算法练习:排列组合之子集合

问题描述 输入一个含有不同数字的序列,输出其所有子集合(含空集).要求:1)集合里元素有序排列:2)输出结果不含有重复集合 举例 输入序列{3,1,2} 输出:{},{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3} 问题分析 可以使用排列组合问题求解的第一种方法:分期摊还.初始化时,结果集合里含有一个空集.当扫描数列时,保留原有集合,同时将当前元素插入现有的所在集合中,从而形成新的集合.详见后面代码的GetSubSetsAmortized函数. 也可以使用第二种方法:f

java排列组合算法代码实现

原文:java排列组合算法代码实现 源代码下载地址:http://www.zuidaima.com/share/1550463479024640.htm java排列组合算法,有需要研究的童鞋可以下载,运行结果如下: package com.zuidaima.test; /** *@author www.zuidaima.com **/ public class Pailie { public static void main(String[] args) { int[] ia = {1, 2,

c# 排列组合算法

//排列组合 public class FullArrange { /// <summary> /// 排列组合 /// </summary> /// <param name="str">字符串</param> /// <param name="splitStr">分割的符号,比如";"</param> /// <returns></returns>