Hdu 1258 Sum It Up :DFS 去重

                            Sum It Up

题目描述:

Given a specified total t and a list of n integers, find all distinct sums using numbers from the list that add up to t. For example, if t = 4, n = 6, and the list is [4, 3, 2, 2, 1, 1], then there are four different sums that equal 4: 4, 3+1, 2+2, and 2+1+1. (A number can be used within a sum as many times as it appears in the list, and a single number counts as a sum.) Your job is to solve this problem in general.
Input
The input will contain one or more test cases, one per line. Each test case contains t, the total, followed by n, the number of integers in the list, followed by n integers x 1 , . . . , x n . If n = 0 it signals the end of the input; otherwise, t will be a positive integer less than 1000, n will be an integer between 1 and 12 (inclusive), and x 1 , . . . , x n will be positive integers less than 100. All numbers will be separated by exactly one space. The numbers in each list appear in nonincreasing order, and there may be repetitions.
Output
For each test case, first output a line containing `Sums of', the total, and a colon. Then output each sum, one per line; if there are no sums, output the line `NONE'. The numbers within each sum must appear in nonincreasing order. A number may be repeated in the sum as many times as it was repeated in the original list. The sums themselves must be sorted in decreasing order based on the numbers appearing in the sum. In other words, the sums must be sorted by their first number; sums with the same first number must be sorted by their second number; sums with the same first two numbers must be sorted by their third number; and so on. Within each test case, all sums must be distinct; the same sum cannot appear twice.
Sample Input
4 6 4 3 2 2 1 1
5 3 2 1 1
400 12 50 50 50 50 50 50 25 25 25 25 25 25
0 0
Sample Output
Sums of 4:
4
3+1
2+2
2+1+1
Sums of 5:
NONE
Sums of 400:
50+50+50+50+50+50+25+25+25+25
50+50+50+50+50+25+25+25+25+25+25

题目意思是,给一个总和数 t , 然后给 n 个数 , 求出所有的组合,注意输出是按照非递增的顺序。

如果无解,就输出 NONE 。

简单的递归搜索,但是要注意去重,如果下一次要搜索的值和当前的相同,同一个位置如果填相同的数,就会出现重复的组合。

举个例子:当前搜索到了 70  65  50 ,剩下的还没搜索的有 50  50  20 ,如果要求总和是 205 的组合,从当前继续搜索下去, 第一个符合的组合是 70  65 50  20,然后 放弃50 会找到 70  65 50  20 ,这样一来就会出现重复的组合,第三个 50 一样的道理。所以在搜索到50之后,要去掉后面一直相同的数。

而且,注意序列是递减排列的,所以不会出现 70  65 50 50 50 10 50 20 的情况。如果不搜索相同的,那会不会漏掉解呢? 不会的,还以 50 为例,如果不选当前的50,选择后面的50得到一个新的解,这个新的解肯定在搜索第一个50的时候就搜索到了,除非后面的 50 搜索不到解,所以不会漏掉解  。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std ;
int t , n , a[13] , cnt ;
int package[13] ;    // 保存组合的数

void DFS( int u , int top , int res ){    // u 当前搜索到第几个, top 当前组合的总和, res 剩下的
	if( res < 0 || u > n+1 )    // 组合数太大 ;u 搜索到头了
		return ;
	if( res == 0 ){    // 恰好找到一组解
		cnt++ ;
		for( int i = 1 ; i <= top ; ++i )
			printf( i == 1 ? "%d" : "+%d" , package[i] ) ;
		cout << endl ;
		return ;
	}
	for( int i = u ; i <= n ; ++i ){                // 从当前到 n ,保证了非递增
		package[top+1] = a[i] ;                 // 把当前数字加入组合
		DFS( i+1 , top+1 , res-a[i] ) ;         // 如果选了这个数,搜索下一个
		while( i+1 <= n && a[i+1] == a[i] )     // 如果下一次搜索的和当前的在同一个位置,会重复
			i++ ;
	}
}

int main(){
	// freopen( "DFS_sum.txt" , "r" , stdin ) ;
	while( cin >> t >> n && n ){
	        int sum = 0 ;
		printf( "Sums of %d:\n" , t ) ;
		for( int i = 1 ; i <= n ; ++i )
			cin >> a[i] , sum += a[i] ; // 所有的加起来都不足 n , 肯定无解,不用搜索了
		if( sum < n ){ cout << "NONE" << endl ; continue ; }
		cnt = 0 ;
		DFS( 1 , 0 , t ) ;
		if( !cnt ) cout << "NONE" << endl ;
	}
	return 0 ;
}


运行时间 0 ms  ,如有错误或者描述不当的地方,敬请指正。


刚开始,我看到这道题目,感觉和以前做过的背包恰好装满的方案总数挺像的,也是递归搜索,不过是二叉的搜索形式,运行时间  16 ms , 比上面的慢 。我想原因可能是二叉的搜索高度更高,递归消耗更大吧,上面的是多叉的搜索形式,搜索树高度更低。

思路和写法几乎都是一样的,就是一次搜索过后,可以再进行几次搜索的问题。


#include <iostream>
#include <cstdio>
using namespace std ;
int t , n , a[13] , cnt ;
int package[13] ;

void DFS( int u , int top , int res ){
	if( res < 0 || u > n+1 )  // 一直卡在这里..... 当前选的是 u , 但是每次的参数是 u+1 , 会忽略掉最后一个 1 
		return ;
	if( res == 0 ){
		cnt++ ;
		for( int i = 1 ; i <= top ; ++i )
			printf( i == 1 ? "%d" : "+%d" , package[i] ) ;
		cout << endl ;
		return ;
	}
	package[top+1] = a[u] ;
	DFS( u+1 , top+1 , res-a[u] ) ;  
	while( u+1 <= n && a[u+1] == a[u] )
		u++ ;
	DFS( u+1 , top , res ) ; // 和背包一样 , top 不加 1 , 意味着不选当前这件物品
}

int main(){
	// freopen( "DFS_sum.txt" , "r" , stdin ) ;
	while( cin >> t >> n && n ){
		printf( "Sums of %d:\n" , t ) ;
		for( int i = 1 ; i <= n ; ++i )
			cin >> a[i] ;
		cnt = 0 ;
		DFS( 1 , 0 , t ) ;
		if( !cnt ) cout << "NONE" << endl ;
	}
	return 0 ;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值