HDU-1258 Sum It Up DFS
给定一个非递减的序列,要求从这些序列中找出一系列的数相加等于要求的数。这题的主要任务就是怎样去重。
现分析如下:
对于给定的两个非递减序列,A, B, C, D 用来映射各个数的位置,2(A) 1(B) 1(C) 1(D) 以及 2(A) 2(B) 2(C) 1(D), 要求求出和为3的表达式。 显然前面的序列递归下去会首先出现A+ B的组合,由于此时A+ B已经等于了3,所以递归就会回溯,此时会形成A+ C的组合,按照正常人的思维,这个解肯定是不能够取的,但是计算机有这么聪明吗?显然还是要我们来设计各种复杂的判断...... 转回正题,我们在递归时有一个属性就是递归的层数,而该题的求解就会与这个层数扯上关系,对于两个相邻且大小相等的点是要特殊考虑的,为什么呢,从上面给定的两个序列可以看出来,(重复就是因为这些相同的数字带来的),而且这个序列是已经相当于排好序的,这点同样很重要。那么对于相同的数(同样一定会是相邻的)在深入的递归过程中是可以取的(可以理解为此时的和还未等于要求值,相同的数仍有利用价值)。那么对于同一层的相同数呢,呵呵,这是不能够取的,为什么呢?首先对于该种组合的状态是不是已经搜索过了,唯一有变化的就是所剩数字的差别了,在此而言,就是相对与上一相同的状态少了这个数而已。这样可能比较抽象,就拿 2 2 2 1 这个序列来说吧,2与后面的2都不能组合,因为其值超过了限定值3,最后和1组合即 A+ D= 3,对于第二个2 即B, 此时B与A是同一层的递归下,如果取 B 的话,那么同当时取 A 就一样了,都是在接下来的序列中寻找和为(3- 2)的组合,然而上次的选择范围是(B, C, D)三个数,这次只剩下(C, D)两个数了,所以可以得出上次搜索的解一定包括这次去搜索得到的解,因此就出现重复了。证毕。
代码如下:
#include <cstdio> #include <cstring> #include <cstdlib> using namespace std; int t, n, num[105], rec[105]; void DFS( int sum, int pos, int cnt, int &ans ) { if( sum== t ) { ans= 1; for( int i= 1; i<= cnt; ++i ) { printf( i== 1? "%d": "+%d", num[ rec[i] ] ); } puts( "" ); return; } for( int i= pos+ 1; i<= n; ++i ) { if( num[i]!= num[i- 1]|| i== pos+ 1 ) { // 前面的就是判定同一层不能计算相邻且大小相等的数,后面的处理不同层的相邻的相同的数 if( t>= sum+ num[i] ) { rec[cnt+ 1]= i; DFS( sum+ num[i], i, cnt+ 1, ans ); } } } } int main() { while( scanf( "%d %d", &t, &n ), t| n ) { int ans= 0; num[0]= -1; for( int i= 1; i<= n; ++i ) { scanf( "%d", &num[i] ); } printf( "Sums of %d:\n", t ); DFS( 0, 0, 0, ans ); if( !ans ) { puts( "NONE" ); } } return 0; }