WEEK3 周记 作业A题——深度优先搜索_选数问题
一、题意
1.简述
n个正整数,找出K个数使得其和为sum。问能够找出多少个这样的数对。
2.输入格式
第一行输入T<=100,表示多少组测试数据。
对于每一组有两行,第一行输入n、K、S,第二行输入n个正整数。
3.输出格式
对于每一组在一行中输出答案。
4.样例
Input
1
10 3 10
1 2 3 4 5 6 7 8 9 10
Output
4
注释
k<=n<=16,并且所有的正整数都能用int表示。
二、算法
主要步骤
可以k次循环,但是显然161616^{16}1616是要超时的。
那就优化,采用dfs搜索的思想,也是求所有kkk个数组合的算法思想。
首先我们将得到的整数序列排序。然后进入递归函数。
利用递归,每层递归循环遍历整数序列,每次循环确定一个数,当这个数加上sumsumsum小于等于SSS时,就进入下一层递归。需要注意的是,下一层递归的循环遍历要从上一层确定的数的后一个开始,这样就能避免从头开始再去遍历前面已经经过的数。当递归深度为i=Ki=Ki=K且sum=Ssum=Ssum=S时,这个时候我们就找到了一个满足条件的KKK元数对,之后回到上一层继续执行。
这样实际上是通过剪枝操作来优化了时间效率。
三、代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int pn[20];
int n,K;
/*long long*/ int S;
int cnt;
int sn[20];
void solve(int i,/*long long*/ int sum,int pos)
{//i是参与构成参数中sum的整数的个数
if(i>K) return;
if(sum==S&&i==K)
{
cnt++;
return;
}
int j;
for(j=pos;j<n;j++)
{
if((sum+pn[j])<=S)
{
sn[j]=1;
solve(i+1,sum+pn[j],j+1);
}
else break;
sn[j]=0;
}
}
int main()
{
int T,i,j;
scanf("%d",&T);
for(i=0;i<T;i++)
{
cnt=0;
scanf("%d%d%d",&n,&K,&S);
for(j=0;j<n;j++)
{
scanf("%d",&pn[j]);
sn[j]=0;
}
sort(pn,pn+n);//从小到大排序
solve(0,0,0);
printf("%d\n",cnt);
}
return 0;
}