使用子集树和排列树两种方式求子集和问题,子集和问题就是集合中的多个正数Xn求和等于指定数k,并使用c++语言给出相应代码以及代码旁边带有注释,不要使用向量简洁明了,并给出思路以及绘制思路树图
时间: 2024-01-22 18:19:24 浏览: 65
思路:
1. 子集树:对于每个元素,有选和不选两种可能,可以看成是一棵二叉树,遍历这棵树,记录下每个叶子结点的和,若有满足条件的叶子结点,则找到了一个解。
2. 排列树:排列树是在子集树的基础上,将每个元素看成是可重复的,即可以出现多次,因为要考虑到重复元素的情况,所以需要加上一个限制条件,即每个元素在当前排列中出现的次数不能超过原集合中该元素的个数。
代码:
1. 子集树
```c++
#include<iostream>
using namespace std;
int n, k, a[100], ans[100], cnt;
void dfs(int x, int sum) { // x表示当前处理到哪个元素,sum表示当前所选元素的和
if (x == n + 1) { // 遍历到叶子结点
if (sum == k) { // 找到一个解
cnt++;
cout << "Solution " << cnt << ": ";
for (int i = 1; i <= n; i++) {
if (ans[i] == 1) cout << a[i] << " ";
}
cout << endl;
}
return;
}
ans[x] = 0; // 不选当前元素
dfs(x + 1, sum);
ans[x] = 1; // 选当前元素
dfs(x + 1, sum + a[x]);
}
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
dfs(1, 0);
if (cnt == 0) cout << "No solution!" << endl;
return 0;
}
```
2. 排列树
```c++
#include<iostream>
using namespace std;
int n, k, a[100], ans[100], cnt, num[100];
void dfs(int x, int sum) { // x表示当前处理到哪个元素,sum表示当前所选元素的和
if (x == n + 1) { // 遍历到叶子结点
if (sum == k) { // 找到一个解
cnt++;
cout << "Solution " << cnt << ": ";
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= ans[i]; j++) { // 输出每个元素出现的次数
cout << a[i] << " ";
}
}
cout << endl;
}
return;
}
for (int i = 0; i <= num[x]; i++) { // 枚举当前元素出现的次数
ans[x] = i;
dfs(x + 1, sum + i * a[x]); // 更新当前和
}
}
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a + 1, a + n + 1); // 排序,以方便后面处理重复元素
int j = 1;
num[1] = 1;
for (int i = 2; i <= n; i++) { // 计算每个元素在原集合中出现的次数
if (a[i] == a[j]) num[j]++;
else {
j++;
a[j] = a[i];
num[j] = 1;
}
}
n = j; // 更新元素个数
dfs(1, 0);
if (cnt == 0) cout << "No solution!" << endl;
return 0;
}
```
思路树图:
1. 子集树
```
1
/ \
2 2
/ \ / \
3 3 3 3
/ \ / \ / \ / \
4 4 4 4 4 4 4 4
```
2. 排列树
```
1(0)
/ | \
1(1) 1(2) 1(3)
/ \ / \ / \
2(0) 2(1) 2(0) 2(1) 2(0) 2(1)
/ \ / \ / \ / \ / \ / \
3(0) 3(1) 3(0)3(1)3(0)3(1)3(0)3(1)3(0)3(1)
```
其中,数字表示当前处理的元素下标,括号中的数字表示该元素当前已经选择的次数。
阅读全文