题意:
给定n个正整数,选择其中K个加起来等于S。问有多少种方法?
输入:
第一行,整数T< = 100,表示测试用例的数量。对于每种情况,都有两行。第一行,三个整数表示n,K和S。第二行,n 个整数表示这n个正整数。
输出:
对于每种情况,一个整数都处于独立的一行中表示答案。
输入样例:
输出样例:
解题思路:
这是一个经典的dfs回溯思想的题目,本质上是递归,从起点开始搜索目标,遇到障碍再折回上一节点。这里需要做一次剪枝操作,当选的数的个数超过K或者选的数的和超过sum时,这次搜索已经是注定失败的了,此时应当及时终止,体现在代码中就是递归的结束条件。if (num>k||sum>s)时就可以及时return了。否则在搜索到条件满足时对统计量ans递增,然后继续递归搜寻其他的方案。
注意事项:
1、无论是满足num>k||sum>s的条件还是num= =k&&sum= =s的条件,都需要及时return,因为两者一个代表搜索失败,一个代表搜索成功,均无需继续。
2、一般把需要的数组开在main外面,即静态区,这是一个常用的小技巧,根据题意开一个足够大的数组,而不是开在堆区或栈区。
总结:
数据结构课程着重讲了bfs遍历,dfs相对练习比较少。上一节课讲解的bfs算法相对比较固定一些,总体都是队列的操作,再加上不同维数的vis、dis数组等。这次的dfs算法感觉更灵活一些,练习的也比较少,可以用栈或递归的方式实现,但好像一般目前遇到的都更多采用递归的方式,递归的几个结束条件的设置尤其需要注意。
参考代码:
#include <iostream>
using namespace std;
int a[20];
int n,k,s;
int ans=0;
void dfs(int num,int sum,int position){
if (num>k||sum>s) {
return;
}
if (num==k&&sum==s) {
ans++;
return;
}
for (int j=position; j<n; j++) {
dfs(num+1, sum+a[j], j+1);
}
}
int main(int argc, const char * argv[]) {
int t;cin>>t;
while (t--) {
ans=0;
cin>>n>>k>>s;
for (int i=0; i<n; i++) {
cin>>a[i];
}
dfs(0,0,0);
cout<<ans<<endl;
}
return 0;
}