抽象DFS深度优先搜索

抽象形式的dfs

例题引入

给定n个整数,要求选出K个数,使得选出来的K个数的和为sum

借助dfs来解决这个问题。对于每一个数,枚举选或者不选两种情况,可以用dfs思想来完成这样的枚举过程。

在搜索过程中,用S来记录当前选择的数值总和,k来记录选择的数的个数,deep表示当前正在枚举第几个数是否选择。

第一层dfs的时候,我们可以枚举是否选第一个数,如果选第一个数则让S加上第一个数且k加一,dfs进入下一层;否则dfs直接进入到下一层。当然,这里我们还需要借助全局变量、参数或修改数组中元素的值等方式来标识出当前的层数,为了减少篇幅,在下文中直接忽略了。

在第二层,对第二个数做同样的处理,dfs的过程中记录已经选取的数的个数,如果已经选取了k个数,判断S的值是否等于sum,对于每一层,我们都有两个选择——选和不选。不同的选择都会使得搜索进入完全不同的分支继续进行搜索。

这个搜索的过程形象化就是搜索树🌲

dfs看起来像是在图上的搜索算法,如果没有看到图的存在的话,就是抽象形式dfs的特点。

我们可以根据搜索状态构建一张抽象的图,图上的一个顶点就是一个状态,而图上的边就是状态之间的转移关系(进一步搜索回溯)。虽然dfs是在这张抽象的图上进行的,但我们不必把这张图真正的建立出来。

从1,2,3,4,5中选若干个数使和为9,有多少种方案?

#include <iostream>
using namespace std;
int n,k,sum,ans;
int a[100];
//i表示正在选取第几个数,cnt表示已经选取了几个数,s表示选取了那几个数的和
void dfs(int i,int cnt,int s){
  if(i==n){
    if(s==sum&&cnt==k){
      ans++;
    }
    return;
  }
  dfs(i+1,cnt,s);
  dfs(i+1,cnt+1,s+a[i]);
}
int main()
{
  cin>>n>>k>>sum;
  for(int i=0;i<n;i++){
    cin>>a[i];
  }
  dfs(0,0,0);
  cout<<ans<<endl;
} // namespace std;

输入

5 3 9
1 2 3 4 5

输出

2

还有一种搜索策略。

之前的方法是每次去抉择是否选第i个数,现在我们的策略是从剩下的数中选择一个数。比如有5个数1,2,3,4,5,如果选择了1,那么剩下2,3,4,5四个数;如果选择了2,那么剩下1,3,4,5四个数,还可以选择3……;选择4……;选择5……。

代码实现起来很简单,我们标记每个数的是否被选择了。我们用s表示选出来的数的和,cnt表示选出来的数的个数。

#include <iostream>
using namespace std;
int a[40];
bool xuan[40];
int n,k,sum,ans;
void dfs(int s,int cnt){
    if(s==sum&&cnt==k){
        ans++;
    }
    for(int i=0;i<n;i++){
        if(!xuan[i]){
            xuan[i]=1;
            dfs(s+a[i],cnt+1);
            xuan[i]=0;
        }
    }
}
int main(){
    cin>>n>>k>>sum;
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    ans=0;
    dfs(0,0);
    cout<<ans<<endl;
    return 0;
}

输入

5 3 9
1 2 3 4 5

输出

12

两次输出不一样是因为第二次搜索中2,3,4和3,2,4等被看作是不同的方案,没有去重。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lydia.na

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值