题目描述
Given n positive numbers, ZJM can select exactly K of them that sums to S. Now ZJM wonders how many ways to get it!
Input
The first line, an integer T<=100<=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, nn integers indicate the positive numbers.
Output
For each case, an integer indicate the answer in a independent line.
样例
Example
input
1
10 3 10
1 2 3 4 5 6 7 8 9 10output
4
思路分析
这是一个子集枚举的问题,可以枚举所有的子集(dfs思想),判断K个数和为S的情况有多少,但显然比较暴力,复杂度高。根据题目要求,我们可以知道当子集的元素个数大于K或元素和大于S时,再继续走下去是没有意义的,所以进行可行性剪枝,当出现以上两种情况时,回溯。
void dfs(int k,int sum,int tag)
{
if(k==K&&sum==S)
{
count++;
return;
}
//可行性剪枝
if(k>K||sum>S) return;
for(int i=tag;i<N;i++)//回溯后,选取下一个元素加入子集继续判断
{
dfs(k+1,sum+num[i],i+1); //按顺序从集合中选一个数加入子集,i为当前在num数组中选取的元素下标
}
}
总结
当我们用这种暴力枚举做法时,要考虑怎么样剪枝,去掉一些不可能的情况,可以大大降低复杂度!
AC代码
#include<iostream>
using namespace std;
int N,K,S,count;
int num[16];
void dfs(int k,int sum,int tag)
{
if(k==K&&sum==S)
{
count++;
return;
}
//可行性剪枝
if(k>K||sum>S) return;
for(int i=tag;i<N;i++)//回溯后,选取下一个元素加入子集继续判断
{
dfs(k+1,sum+num[i],i+1); //按顺序从集合中选一个数加入子集,i为当前在num数组中选取的元素下标
}
}
int main()
{
int T;
cin>>T;
for(int i=0;i<T;i++)
{
cin>>N>>K>>S;
count=0;//记得初始化
for(int i=0;i<N;i++)
{
cin>>num[i];
}
dfs(0,0,0);
cout<<count<<endl;
}
return 0;
}