算法第五章作业
1.你对回溯算法的理解(2分)
基本概念:回溯法是一个类似枚举搜索的过程,在这个过程中找到解,如果在搜索到一的结点的时候不满足题目要求,就回溯到上一个结点,再搜索别的路径是否为最优解。
特点:采用深度优先的搜索方法,若需要求问题的所有解,则回溯到根结点,若只需求一个解,则只需搜索到一个点即可结束。
解题步骤:首先确定问题的解空间,问题的解空间应至少包含问题的一个(最优)解;再确定结点的扩展搜索规则,子集数or排列树;以深度优先方式搜索解空间,并用剪枝函数,使搜索效率更高。
剪枝函数:用约束函数在扩展结点处剪去不满足约束的子树;和用限界函数剪去得不到最优解的子树。
2.请说明“子集和”问题的解空间结构和约束函数(2分)
7-1 子集和问题 (50 分)
设集合S={x1,x2,…,xn}是一个正整数集合,c是一个正整数,子集和问题判定是否存在S的一个子集S1,使S1中的元素之和为c。试设计一个解子集和问题的回溯法。
输入格式:
输入数据第1行有2个正整数n和c,n表示S的大小,c是子集和的目标值。接下来的1行中,有n个正整数,表示集合S中的元素。 是子集和的目标值。接下来的1 行中,有n个正整数,表示集合S中的元素。
输出格式:
输出子集和问题的解,以空格分隔,最后一个输出的后面有空格。当问题无解时,输出“No Solution!”。
输入样例:
在这里给出一组输入。例如:
5 10
2 2 6 5 4
输出样例:
在这里给出相应的输出。例如:
2 2 6
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 int n, c, flag = 1; 5 int a[10005], choice[10005], record[10005],sum = 0; 6 void Backtrack(int t) { 7 if (!flag) 8 return; 9 else { 10 for (int i = t; i < n; i ++ ) { 11 if (!choice[i] && (c - a[i]) >= 0) { 12 c -= a[i]; 13 choice[i] = 1; 14 record[i] = a[i]; 15 if (c == 0) { 16 for(int t = 0; t < 10005; t ++ ) { 17 if(record[t]!=0) 18 cout << record[t] << ' '; 19 } 20 cout << endl; 21 flag = 0; 22 return; 23 } 24 else { 25 Backtrack(t+1); 26 if (!flag) return; 27 c += a[i]; 28 choice[i] = 0; 29 record[i] = 0; 30 } 31 } 32 } 33 } 34 } 35 int main() { 36 cin >> n >> c; 37 for (int i = 0; i < n; i ++ ) { 38 cin >>a[i]; 39 sum += a[i]; 40 } 41 if (sum < c) { 42 cout << "No Solution!" << endl; 43 return 0; 44 } 45 memset(choice, 0, sizeof(choice)); 46 memset(record, 0, sizeof(record)); 47 Backtrack(0); 48 if (flag) 49 cout << "No Solution!" << endl; 50 return 0; 51 }
/*从第一个元素开始,如果此时当前的元素不在集合内的话
将这个元素加到子集当中来(用record数组标记) ,将sum加上这个元素的值。
然后判断如果sum恰好为目标值c的话,就返回正值并且打印结果。
如果sum > c 的话则舍弃当前这个元素,修改标记数组,并且将sum减去这个元素的值。
只要还有元素没有判断就继续选择。直到第n个元素,如果第n个元素判断完还没有找到解的话,
就回溯到上一次选择的那个点,将其从集合里面删除并从它后一个点继续重复前面的操作。*/
解空间结构:子集树的解空间的个数是指数级别的,为2^n
剪枝函数:如果回溯的时候回溯到了第一个元素之前,则要么所有元素都加入到集合都不够,或者是所有的情况都找过了还是没有解决方案,这个时候返回无解,这部分将被舍弃,回溯寻找其他路径。
3.请说明在本章学习过程中遇到的问题及结对编程的情况(1分)
当我逐步了解回溯法的框架和含义后,题目也更好理解了。我的队友总是非常耐心地向我讲解,帮助我对回溯法了解得更透彻了。