【DFS题解】凑数

题目描述

给定 n(1<=n<=100)个互不相同的正整数 a1,a2,a3...an,将之排成一行(顺序)。再给你一个要求的值m(m<=1000)。你需要在每个 ai(ai<=100) 前加上一个加号(+)或减号(-),使这n个数字组成一个算式。请问是否存在一种添加符号的方案,使该算式的值为m?如果存在,请输出Yes,否则输出No。

例如,给定 n = 5, m = 17, a1 = 1, a2 = 4, a3 = 5, a4 = 9, a5 = 8,则 −a1 − a2 + a3 + a4 + a5 = 17。

解题思路

这道题目类型明显可以用深搜(DFS)来解决,因为每次添加的符号不是固定的(数据也很水),详见下图。

以题目中的例子为例
0(初始值)
    +1 ans=1
        继续执行...
    -1 ans=-1
        +4 ans=3
            继续执行...
        -4 ans=-5
            +5 ans=0
                +9 ans=9
                    +8 ans=17
                        !!这时候答案出现了,直接return,flag标记为真,这样程序会自动回溯,不必继续执行
                -9 ans=-9
                    继续执行...
            -5 ans=-10
                继续执行...

相信很多初学搜索的人(包括蒟蒻我) 看着都很懵,其实这就是一个基本的二叉树,属于图论的一种,比较考验思维能力,形象来说就是不撞南墙不回头对时间复杂度很友好(常数原则)

代码实现

用dfs的深搜解决,可以每一次递归四次运算,求出结果就return 。

#include<bits/stdc++.h>
using namespace std;
int a[1005] {}, n,m;
bool flag = 0;
void dfs(int pos, int ans, int cnt)
{
    if(cnt>n) { //符合条件就返回
        if(ans == m)    flag = 1; //标记
        return;
    }
    dfs(pos+1,ans+a[pos],cnt+1); //递归两种运算
    dfs(pos+1,ans-a[pos],cnt+1);
}
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i ++)    cin >> a[i];
    dfs(1,0,0);
    puts(flag? "Yes":"No"); //判断标记
    return 0;
}

这是一个很简单的深搜答案,但是还可以优化。

优化

return之后还要执行完其他的分支,比较费时间(不过一般不影响),这里介绍exit语句,可以直接在函数内结束程序,也可以做异常报告。

exit

语法格式

exit(返回值); //0的话直接结束,负数的话抛异常

比如下面这个例子,如果数组里有要找的数就直接输出并结束,省时间。

int a[1005]{},x; //数组和要找的数
//其他代码...
void find(int pos)
{
    if(a[pos]==x) {
        cout << pos << endl;
        exit(0); //直接结束
    }
    find(pos+1); //继续找
}

在本例子里可能体现不出来,大家看深搜的代码就更有感受了。

//前面略
void dfs(int pos, int ans, int cnt)
{
    if(cnt>n) { //符合条件就返回
        if(ans == m) {
            puts("Yes");
            exit(0); //直接结束,省时间
        }
        return; //别忘了还要return
    }
    dfs(pos+1,ans+a[pos],cnt+1); //递归两种运算
    dfs(pos+1,ans-a[pos],cnt+1);
}
int main()
{
    //略...
    return puts("NO"),0; //直接输出错,比之前省时间
}

拓展

这道题要求按照顺序添加符号,如果这道题要求可以改变顺序的话该怎么办呢?next_permutation函数!!!

next_permutation

语法格式

next_permutation(数组首地址,数组尾地址); //进行区间升序排列,如果全排完返回0,否则返回正值

那么代码就变成这样

#include<bits/stdc++.h>
using namespace std;
int a[1005] {}, n,m;
bool flag = 0;
void dfs(int pos, int ans, int cnt)
{
    if(cnt>n) { //符合条件就返回
        if(ans == m) {
            puts("Yes");
            exit(0); //直接结束,省时间
        }
        return; //别忘了还要return
    }
    dfs(pos+1,ans+a[pos],cnt+1); //递归四种运算
    dfs(pos+1,ans-a[pos],cnt+1);
}
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i ++)    cin >> a[i];
    do{
        dfs(1,0,0); //深搜
    }while(next_permutation(a+1,a+n+1)); //不断排列
    return puts("No"),0;
}

结束

今天的学习就到这里啦,一道简单的深搜应用题,希望大家能有收获!

掰掰ヾ(•ω•`)o

可以转载,请注明作者和原文链接

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值