选数问题(dfs)

问题描述

Given nnn positive numbers, ZJM can select exactly KKK of them that sums to SSS. Now ZJM wonders how many ways to get it!(nnn个数中选KKK个数,其和等于SSS的方案有多少个)

Input

The first line, an integer T≤100T\le 100T100, indicates the number of test cases. For each case, there are two lines. The first line, three integers indicate nnn, KKK and SSS. The second line, nnn integers indicate the positive numbers.

Output

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

Sample Input

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

Sample Input

4

Note

Remember that k≤n≤16k\le n\le 16kn16 and all numbers can be stored in 32-bit integer

解题思路

\hspace{17pt}这个题是一个典型的子集枚举问题,为了防止TLE,我们应该考虑一些特殊情况进行合理剪枝。
\hspace{17pt}我们对这nnn个数进行dfs,每个数有两种选择,取或者不取。那么我们就有了两种可行性剪枝方案:1.1.1.选择数的个数大于KKK2.2.2.已经选择数的总和大于SSS(由于所有数都是正数,大于SSS就出现错误)。
\hspace{17pt}在下面的代码中使用list是为了方便调试,可以查看合法方案,单对此题来说,并没有使用list的必要,再开一个参数记录已经选择的个数即可。

完整代码

//#pragma GCC optimize(2)//比赛禁止使用!
//#pragma G++ optimize(2)
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <list>
using namespace std;

const int maxn=20;
int T,n,k,s,ans,a[maxn];
list<int> l;//使用list就是为了查看当前有那些值
void dfs(int step,int sum)//step代表当前到了第几个数,sum是目前list中数的和
{
    if (sum == s && l.size() == k){
        ans++;
        //for(auto it:l) cout<<it<<' ';//查看组成情况
        //cout<<endl;
        return;
    }//当达到s并且数字个数达到k
    if(step>n || l.size()>=k || sum>=s) return;//搜过了(此处注意应该是'>'号),个数大于k了,总和大于sum了,则返回

    dfs(step+1,sum);//不取当前数,向后搜索
    l.push_back(a[step]);//将当前数加入list中
    dfs(step+1,sum+a[step]);//取当前数,向后搜索
    l.pop_back();//将取的数弹出
}
int getint()
{
    int x=0,s=1;
    char ch=' ';
    while(ch<'0' || ch>'9')
    {
        ch=getchar();
        if(ch=='-') s=-1;
    }
    while(ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*s;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin>>T;
    while(T--){
        cin>>n>>k>>s;

        ans=0; memset(a,0,sizeof(a));l.clear();

        for (int i=1; i<=n; i++)
            cin>>a[i];

        dfs(1,0);

        cout<<ans<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值