算法練習:排列組合之組合和


問題描述

給出一組不同的正整數序列和一個目標值,求出所有可能的組合,使得組合里所有元素和為目標值。要求:

1)每個組合里的元素按照升序排列。

2)輸出組合里不含有重復的組合。

3)輸入序列中的整數可以多次使用。

 

舉例:

輸入{2347},目標值為7

輸出{7}{223}{34}

 

問題分析

為了讓輸出元素按升序排列,可對輸入序列進行排序。同這里我們使用遞歸的方法來解決這個組合問題,即典型的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


 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com