题目描述
给定n个正数,ZJM可以精确地选择其中的K个使它们之和为S。现在ZJM想知道有多少种方法可以得到它!
Input
第一行是一个整数T<=100,表示测试用例的数量。对于每种情况,有两行。第一行,三个整数分别表示n, K和S。第二行,n个正整数。
Output
对于每种情况,一个整数在独立的行中表示答案。
Example
Input:
1
10 3 10
1 2 3 4 5 6 7 8 9 10
Output:
4
Note
记住,k<=n<=16k<=n<=16,所有数字都可以存储为32位整数。
解题思路
本题的思路可以由简单到复杂一步步深入。首先可想到的最“硬核”的解题方法就是:每个数都有取或不取这两种可能,因此可以直接用最简单的递归解决。但考虑到时间复杂度过高的问题,要想办法对算法进行优化。根据递归过程分析,优化的方法可以从两种角度分析:选取数的个数和和的大小,即当选取数的个数超过指定个数,或者和的大小超过指定和时,都可以跳过此次递归。
由于考虑到选数是一个依次进行的过程,因此才选用递归的方法实现。
代码
#include<iostream>
using namespace std;
int n,k,s,num[16];
int number;
void select(int i,int m,int sum,int *num)//i记录数组第几个,m记录选了的个数
{
if(sum==s&&m==k)//找到符合条件的一种情况,number+1
{number++; return ;}
if(sum>s||m>k) return ;//这种情况不符合要求,直接返回
if(i>=n) return; //数组遍历结束,返回
select(i+1,m,sum,num);//没选
select(i+1,m+1,sum+num[i],num);//选了
}
int main()
{
int t,m,sum;
cin>>t;//t是总共测试示例的个数
for(m=0;m<t;m++)
{
number=0;//number是需要输出的取数方法个数,初始为0
sum=0;//当前的和的大小
cin>>n>>k>>s;//数组大小,几个数字求和,以及需要满足的和的大小
for(int i=0;i<n;i++)
cin>>num[i];//数组值
select(0,0,sum,num);
cout<<number<<endl;
}
return 0;
}
反思
做一道题的重要思路是从最简单最容易的想法入手,然后再一步步优化,这样就能保证思路的正确,而且是一种做题的通用思路。在这过程中优化的方式通常会从时间复杂度的方向考虑。