精选算法题(1)——枚举符合要求的算术表达式(DFS、回溯法)

简介: 精选算法题(1)——枚举符合要求的算术表达式(DFS、回溯法)

题目描述:

约定按照自然优先级,并且不使用括号,在数字'0'~'9'之间加入加号'+'或乘号'*’,构成合法的算术表达式。

对于任一给定的整数S,枚举出所有值为S的上述类型表达式。

示例:

图1 示例

解题思路:

本题适合用回溯法和深度优先遍历DFS解决。具体思路如下:


  1. 首先,罗列出所有符合要求的数字,放置在Inum容器中,比如123、12、234等等,前提是要小于整数S;
  2. 定义辅助函数——Calculation,使其读入一个string类型的算术表达式,比如1+3+5+6*7,能返回结果;
  3. 定义辅助函数——findnum,用来寻找下个符合要求的数所在的位置,比如1+2+3,后面可以放置4,也可以放置45,也可以放置456,;
  4. 求解函数DFS:DFS用flag来标记要用乘法还是加法,从Inum中挨个读取,因为0和9一定是单数,所以第一个读取的应该是0,拿到0后存放在str中,str用来标记当前已经有多少个数字在表达式中了,比如01234,那接下来就要找5开头的数字;字符串s用来存放表达式;开始深度优先遍历,id是当前位置,从id+1进入循环,用Calculation判断当前表达式结果是否大于S,好处是可以使遍历过程快速收敛,达到提速目的;若当前表达式符合要求,用findnum函数寻找Inum中符合要求的数字并存储其下标,基于该下标继续调用DFS,进入递归,一层层下去相当于深入到最里面的节点,即深度优先;当DFS执行到最后一个数9,用Calculation计算是否等于S,存储符合要求的表达式,返回上级节点;回归上级节点后,别忘了将str和s重置,比如1+2+3+4不合格了,那就继续执行1+2+3+45,s存放的应该是1+2+3+;当DFS执行完,结果就全出来了,因为每次运算到9后,后面还跟了一次加法和一次乘法,即9+和9*,这样就导致每次结果都存放了两次,因而输出result的时候需要间隔输出。

测试代码:

#include <iostream>
#include <string>
#include <vector>
#include <time.h>
using namespace std;
vector<int> num = { 0,1,2,3,4,5,6,7,8,9 };
vector<string> result;
int number;
// 计算string表示的算术表达式
int Calculation(string& exp, int start, int end)
{
  int am = 0, md = 0;
  // 标记最后出现的加减乘除号位置(不在括号内)
  for (int i = start; i < end; ++i)
  {
    if (exp[i] == '+')
      am = i;
    if (exp[i] == '*')
      md = i;
  }
  // 若有加减号,则将符号前面的内容和后面的内容相加或减
  if (am > start) {
    return Calculation(exp, start, am) + Calculation(exp, am + 1, end);
  }
  // 若有乘除号,则将符号前面的内容和后面的内容相乘或者除
  else if (md > start)
  {
    return Calculation(exp, start, md) * Calculation(exp, md + 1, end);     
  }
  // 若没有加减乘除,也没有内置括号,说明这部分内容是数字
  else
    return stoi(exp.substr(start, end - start));
}
// 寻找下个数在Inum中可能出现的位置
bool findnum(string str,vector<int> &Inum,int &id)
{
  // fin是最后一个字符
  char fin = str[str.size() - 1];
  // 确定下个数的位置
  for (int i = id; i < Inum.size(); ++i)
  {
    string num = to_string(Inum[i]);
    if (num[0] == fin+1)
    {
      id = i;
      return true;
    }
    // 最后一个数字如果是9,说明结束了,不用再计算了
    else if(fin=='9'){
      return false;
    }
  }
  return false;
}
// 回溯法求解
bool DFS(string &str, vector<int>&Inum, int id,string s,bool flag)
{
  str += to_string(Inum[id]);
  if (flag)
  {
    s += to_string(Inum[id]) + "+";
  }
  else {
    s += to_string(Inum[id]) + "*";
  }
  string tstr = str;
  string ts = s;
  // 深度优先遍历
  for (int i = id + 1; i < Inum.size(); ++i)
  {
    // 若某个节点已经大于number,则直接跳过,使计算过程收敛,达到提速目的
    if (Calculation(s, 0, int(s.size() - 1)) < number)
    {
      if (findnum(str, Inum, i))
      {
        DFS(str, Inum, i, s, true);
        DFS(str, Inum, i, s, false);
      }
      else
        break;
      // 回归当前节点
      str = tstr;
      s = ts;
    }
  }
  // 若有结果且有9,说明算术表达式合理且成立
  if (Calculation(s,0,int(s.size() - 1))==number&& str.find('9')!= string::npos)
  {
    result.push_back(s.substr(0,s.size()-1));
    return true;
  }
  return false;
}
int main()
{
  while (cin >> number)
  {
    // Inum存放所有可能出现的数字
    vector<int> Inum = { 0 };
    for (int i = 1; i < 10; ++i)
    {
      int temp = num[i];
      Inum.push_back(temp);
      int k = 10;
      for (int j = i + 1; j < 10; ++j)
      {
        temp = temp * 10 + num[j];
        if (temp > number)
        {
          break;
        }
        Inum.push_back(temp);
      }
    }
    string snum;
    string s;
    // 计算
    clock_t start, end;
    start = clock();
    DFS(snum, Inum, 0, s, true);
    DFS(snum, Inum, 0, s, false);
    end = clock();
    cout << "time:" << (end - start) / CLOCKS_PER_SEC << endl;
    // 输出结果
    if (result.size() != 0)
    {
      for (auto i = 0; i < result.size(); i=i+2)
      {
        cout << result[i] << endl;
      }
    }
    else {
      cout << "no result!" << endl;
    }
    result.clear();
  }
}

测试结果:

图2 结果

      如果代码有什么需要改进的或者有什么bug,欢迎评论留言,我会及时更正以免误导他人~

      如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

相关文章
|
1月前
|
算法 测试技术 定位技术
数据结构与算法——DFS(深度优先搜索)
数据结构与算法——DFS(深度优先搜索)
|
3月前
|
算法
DFS算法的实现
DFS算法的实现
60 3
|
5月前
|
存储 算法 Java
Java中,树与图的算法涉及二叉树的前序、中序、后序遍历以及DFS和BFS搜索。
【6月更文挑战第21天】Java中,树与图的算法涉及二叉树的前序、中序、后序遍历以及DFS和BFS搜索。二叉树遍历通过访问根、左、右子节点实现。DFS采用递归遍历图的节点,而BFS利用队列按层次访问。以下是简化的代码片段:[Java代码略]
46 4
|
1月前
|
存储 算法 Java
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
前缀(波兰)表达式、中缀表达式和后缀(逆波兰)表达式的基本概念、计算机求值方法,以及如何将中缀表达式转换为后缀表达式,并提供了相应的Java代码实现和测试结果。
44 0
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
|
5月前
|
算法
数据结构与算法-DFS+BFS篇(迷宫问题)
数据结构与算法-DFS+BFS篇(迷宫问题)
63 3
|
5月前
|
算法 JavaScript 程序员
程序员必知:《程序设计与算法(二)算法基础》《第一周枚举》熄灯问题POJ
程序员必知:《程序设计与算法(二)算法基础》《第一周枚举》熄灯问题POJ
32 0
|
5月前
|
算法 Java
Java数据结构与算法:图算法之深度优先搜索(DFS)
Java数据结构与算法:图算法之深度优先搜索(DFS)
|
6月前
|
算法 C++ 容器
黑马c++ STL常用算法 笔记(5) 常用算术生成算法
黑马c++ STL常用算法 笔记(5) 常用算术生成算法
|
5月前
|
算法 索引
DFS算法及应用(二)
回溯:回溯就是DFS的一种,在搜索探索过程中寻找问题的解,当发现不满足求解条件时,就回溯返回,尝试其他路径。