/*
description:
输入两个整数n和sum,从数列1,2,3.......n 中随意取几个数,使其和等于sum,要求将其中所有的可能组合列出来。
方法2: 回溯减枝
这个问题属于子集和问题(也是背包问题)。本程序采用回溯法+剪枝,其中X数组是解向量,t=∑(1,..,k-1)Wi*Xi, r=∑(k,..,n)Wi,且
若t+Wk+W(k+1)<=M,则Xk=true,递归左儿子(X1,X2,..,X(k-1),1);否则剪枝;
若t+r-Wk>=M && t+W(k+1)<=M,则置Xk=0,递归右儿子(X1,X2,..,X(k-1),0);否则剪枝;
本题中W数组就是(1,2,..,n),所以直接用k代替WK值。
<<编程之法>>
参考:https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/02.03.md
author: JasonZhou
date: 2016-03-15
*/
#include<iostream>
#include <list>
using namespace std;
//输入t, r, 尝试Wk
void SumOfkNumber(int t, int k, int r, int& M, bool& flag, bool* X)
{
X[k] = true; // 选第k个数
if (t + k == M) // 若找到一个和为M,则设置解向量的标志位,输出解
{
flag = true;
for (int i = 1; i < k; ++i)
{
if (X[i] == 1)
{
printf("%d + ", i);
}
}
if (X[k] == 1)
{
printf("%d", k);
}
printf("\n");
}
else
{ // 若第k+1个数满足条件,则递归左子树
if (t + k + (k + 1) <= M)
{
SumOfkNumber(t + k, k + 1, r - k, M, flag, X);
}
// 若不选第k个数,选第k+1个数满足条件,则递归右子树
if ((t + r - k >= M) && (t + (k + 1) <= M))
{
X[k] = false;
SumOfkNumber(t, k + 1, r - k, M, flag, X);
}
}
}
void search(int& N, int& M)
{
// 初始化解空间
bool* X = (bool*)malloc(sizeof(bool)* (N + 1));
memset(X, false, sizeof(bool)* (N + 1));
int sum = (N + 1) * N * 0.5f;
if (1 > M || sum < M) // 预先排除无解情况
{
printf("not found\n");
return;
}
bool f = false;
SumOfkNumber(0, 1, sum, M, f, X);
if (!f)
{
printf("not found\n");
}
free(X);
}
int main()
{
int n=9,sum=10;
search(n,sum);
return 0;
}
结果