我們直接來看乙個問題。
給定乙個由n個整數組成的集合,其中n <= 40,每乙個整數都小於或等於
我們用例子來說明。
【例題1】
輸入集合為 ,n=6, s = 42
也就是從集合的6個元素中選擇適當的元素組成乙個子集,使得子集所有元素的和最大,並且該和小於或等於42。
【答案】該子集為,它的元素和為34+5+2=41,就是滿足條件的最大子集和。
現在改變s的值,請你自己做下面的例子。
【例題2】
輸入集合為 ,n=6, s = 10
分析思路
最直接的演算法就是暴力搜尋:找到n個整數的所有可能的子集,檢查子集的元素和否小於或等於s,並從中找出元素和為最大的子集。
想法簡單暴力,我喜歡!
但是它是否可行呢?
我們來看看這種演算法的時間複雜度。
我們知道n個元素的集合,它的所有子集個數有
當n的值比較大,
折衷演算法
折衷演算法是一種搜尋技術。當輸入值比較小,但也不是小到可以採用暴力搜尋演算法時,往往可以採用折衷演算法。
折衷演算法的基本思路是將問題分成兩部分,分別單獨解決它們,然後合併,找出最後的解。將問題分成兩部分後,問題的規模比原來小,並使複雜度上得到簡化,因而有可能解決問題。
我們使用折衷演算法來解決上面的問題。下面是演算法的描述:
1)將原來的整數集合拆分為2個子集,例如a和b,a中包含前[n / 2]個整數,b中包括剩下的整數。
2)從集合a中找出所有可能的整數子集和,並且儲存在陣列x中。類似地,把集合b中所有可能的整數子集和,並儲存在陣列y中。這樣,陣列x和y的元素個數最多為
3)現在合併這兩個子問題:從陣列x和y中找出組合,使它們的和小於或等於s。
在第3)步中,一種合併方式是簡單地遍歷陣列y的所有元素,並關聯於陣列x的每個元素,以檢查每種關聯得到的和是否小於或等於s。這樣的時間複雜度為
怎麼辦呢?
為了使它不那麼複雜,可以先對陣列y進行排序,然後再依次迭代x的每個元素,對於x中的每個元素 x,使用二分搜尋法在y中找到最大元素 y,使得x + y <= s。
這裡的二分搜尋法有助於將複雜度從
也就是等於
因此我們的最終執行時間複雜度為
看到沒有?與原有演算法相比,它大大降低了時間複雜度,使得用該演算法解決問題變得可行。
**實現
我們採用c++來實現該演算法,你可以嘗試執行下面的程式。
#include
using namespace std;
//定義型別的別名
typedef long long int llit;
//定義陣列x和y,分別存放一半集合元素組成的可能的元素和
llit x[2000005],y[2000005];
//計算集合a中所有可能子集的元素之和,並且儲存在陣列x中
void calcsubarray(llit a, llit x, int n, int c)
} //找到小於或等於s的最大可能元素和
llit solvesubsetsum(llit a, int n, llit s)
} return max;
} //主程式
int main()
; int n=sizeof(a)/sizeof(a[0]);
llit s = 10;
printf("largest value smaller than or equal to given "
"sum is %lld\n", solvesubsetsum(a,n,s));
return 0;
}
集合最大子集演算法 C4 5演算法2
設s是s個資料樣本的集合。假定類標號ci i 1,m 具有m個不同的值,設si是類ci中的樣本數。對乙個給定的樣本分類所需的期望資訊由下式給出 i是任意樣本屬於c i的概率,並用s i s來估計。設屬性a具有v個子集s i,s v 其中,s j包含s中這樣一些樣本,它們在a上具有值a j。如果a選作...
演算法 集合的子集
給定乙個集合,輸出它的所有子集。示例 給定集合 1,2,3 應該輸出 增量構造法,每次選擇乙個元素放到集合中,每次操作的結果即是乙個子集。遞迴操作,每次向當前集合中新增乙個比當前集合中最大的元素大1的數。from future import print function defprint subse...
最大子集和
選出乙個陣列裡的乙個連續子集,使這個子集相加的和為所有子集中最大。舉例 給定乙個陣列為 2,1,3,4,1,2,1,5,4 輸出結果為 4,1,2,1 他的元素相加和為6為所有子集中最大。函式原型 retcode maxsubarray int inputarr,int inputlen,int o...