# Backtracking# Recursion# Set# Combinatorial

c130 - 00574 - Sum It Up

🔗 前往 ZeroJudge 原題

題目描述

題目要求找出一個給定的正整數集合,所有加總等於目標值 t 的子集組合。輸出所有可能的組合,並按照特定規則排序。如果找不到任何組合,則輸出 "NONE"。

解題思路

本題採用遞迴回溯法 (Backtracking) 解決。dfs 函數從給定的數組中選擇數字,嘗試將它們加總到目標值 t。

  • 遞迴的基礎情況是當目前的總和等於 t 時,將找到的子集存儲到一個 set 中。使用 set 可以自動去除重複的組合。
  • 在遞迴過程中,從當前索引 it 開始,遍歷數組中的每個數字。如果將當前數字加到總和中不會超過 t,則將其包含在當前子集中,並遞迴調用 dfs 函數。
  • 為了避免重複使用同一個數字,遞迴調用時,索引 it 遞增。
  • 程式碼使用一個 stk 陣列來儲存當前正在考慮的子集,並在找到有效組合時,將其負數存入 set 中,因為題目要求輸出時數字由大到小排列。

複雜度分析

  • 時間複雜度: O(2^n),其中 n 是輸入數組的大小。在最壞的情況下,需要遍歷所有可能的子集。
  • 空間複雜度: O(n + k),其中 n 是輸入數組的大小,k 是找到的有效子集的數量。stk 陣列佔用 O(n) 空間,set 佔用 O(k) 空間。

程式碼

#include <bits/stdc++.h>
using namespace std;
int t,n,ca,a[13],stk[13],x,y;
set <vector <int>> st;
set <vector <int>> :: iterator it;
void dfs(int it,int sum,int sz){
	if(sum==t){
		vector <int> a;
		for(int i=0;i<sz;++i){
			a.push_back(-stk[i]);
		}
		st.insert(a);
		return;
	} 
	for(int i=it+1;i<=n;++i){
		if(sum+a[i]<=t){
			stk[sz]=a[i];
			dfs(i,sum+a[i],sz+1);
		}
	}
}
int main(){
	cin.tie(0); ios::sync_with_stdio(0);
	while(cin >> t >> n){
		if(t==0&&n==0)break;
		st.clear();
		for(int i=1;i<=n;++i)
			cin >> a[i];
		cout << "Sums of " << t << ":\n";
		dfs(0,0,0);
		if(st.empty()){
			cout << "NONE\n";
		}
		for(it=st.begin();it!=st.end();++it){
			for(int i=0;i<(*it).size();++i){
				if(i)cout << "+";
				cout << -(*it)[i];
			}
			cout << "\n";
		}
	}
}

Discussion