Week3--作业--A - 选数问题 [DFS]

题目描述

Given n positive numbers, ZJM can select exactly K of them that sums to S. Now ZJM wonders how many ways to get it!

输入

   The first line, an integer T<=100, indicates the number of test cases. For each case, there are two lines. The first line, three integers indicate n, K and S. The second line, n integers indicate the positive numbers.

输出

  For each case, an integer indicate the answer in a independent line.

样例输入

1
10 3 10
1 2 3 4 5 6 7 8 9 10

样例输出

4

思路

综述

这个题有两种思路:

第一种(暴力点的):

需要从n个数中选出K个,构成和为S;
自然可以枚举n的所有子集,依次判定是否满足题意;
复杂度:O(K*2^n)

第二种:

可以类似隐式图的做法;
只不过这里的每个点是K维的;
利用DFS进行搜索,外加剪枝;
(这里的搜索类似维度的增加,从点的维度是0开始搜索,每到一个点判断是否加入该维度,加加加,加到K维,外加剪枝的条件)

剪枝条件:

条件1:节点的个数多于K且和不等于S,退出;
条件2:节点的个数未小于K但是和已经大于S,退出;
满足以上任意一个条件即可;

过程
Step1:输入

数组a存储的是n个数
用STL中的list链表ans来存储结果(单单就解答这个题目而言,没有必要存储所有的结果)

		 a = new int[n];
		for (j = 0; j < n; j++)cin >> a[j];
		list<int> ans;
Step2:搜索

其中tot是全局变量,不用担心形参的释放问题;
退出的条件:
ans链表的个数等于K,并且和为S

	if (ans.size() == K && sum == 0) {
		tot++;
		return;
	} 

剪枝问题:
除了之前的两个外,加上了触碰到了a数组的外面,也就是搜索到n个数之外的。


	if (i >= n) return;
	if (sum < 0 || ans.size()> K) return;

每到一个点,两个方向,一是加上该点继续向下,另一个是不加上该点继续向下;

	solve(i + 1, sum, ans);
	ans.push_back(a[i]);
	solve(i + 1, sum -= a[i], ans);
	ans.pop_back();

总结

深入思考:
单单就这个题而言,没必要加上list;
可以在搜索的过程中,加上一个参数number(int)来记录当前的维度情况,也就是当前选择了多少点。
这样可以最大化的降低复杂度。

代码

#include <iostream>
#include <list>
using namespace std;
int tot = 0;
int K = 0;
int s;
int n;
int* a;
void solve(int i, int sum, list<int>& ans) {
//退出
	if (ans.size() == K && sum == 0) {
		tot++;
		return;
	} 
	//剪枝
	if (i >= n) return;
	if (sum < 0 || ans.size()> K) return;
	//不选
	solve(i + 1, sum, ans);
	ans.push_back(a[i]);
	//选
	solve(i + 1, sum -= a[i], ans);
	ans.pop_back();
}
int main() {
	
	int t, i,j;
	cin >> t;
	for (i = 0; i < t; i++) {
		cin >> n >> K >> s;
		 a = new int[n];
		for (j = 0; j < n; j++)cin >> a[j];
		list<int> ans;
		//开始搜索
		solve(0, s, ans);
		cout << tot << endl;
		tot = 0;
		delete[]a;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值