DFS练习之开心(蓝桥)

DFS深搜知识回顾:深度优先搜索(DFS)之飞机降落-CSDN博客

问题描述

人类的开心程度有高低之分,数字也一样。

给定一个正整数 n,在 n 的数位之间插入 k 个加号,使其变成一个表达式,计算得出的结果就是 n 的一个 k 级开心程度。

例如 n=1234k=1 时,我们可以往 2 和 之间插入一个 号,使其变为 12+34,计算出结果为 46 。那么 46 就是 1234 的一个 级开心程度。

给定 n,k 请你计算出 n 的 k 级开心程度的最大值与最小值之差。

输入格式

一行输入两个正整数 n,k,含义见题面。

输出格式

一行一个整数,表示 n 的 级开心程度的最大值与最小值之差。

样例输入

1234 1

输出样例

189

说明

可以证明 1234 的 1 级开心程度的最大值为 1+234=235,最小值为 12+34=46,故最大值与最小值的差值为 189。

数据范围

对于 20% 的测试样例,1≤n≤10^{9},k=1。

对于 100% 的测试样例,1≤n≤10^{18},1≤k<len(n),其中 len(x) 表示 x 的长度。

运行限制

语言最大运行时间最大运行内存
C++1s512M
C1s512M
Java2s512M
Python33s512M

思路

根据题目举例。给定一个正整数和一个整数 k,我们需要在 n 的数位之间插入 k 个加号,然后计算出所有可能得出的整数和的最大值和最小值,然后输出这两个值的差。

具体,如果n = 1234,k = 1:

    1. 在第 1 和 2 位之间插入一个加号,得到的整数和是 1 + 234 = 235;

    2. 在第 2 和 3 位之间插入一个加号,得到的整数和是 12 + 34 = 46;

    3. 在第 3 和 4 位之间插入一个加号,得到的整数和是 123 + 4 = 127。

显然,上述方案中最大值 235,最小值46,两者之差为 235 - 46 = 189。

解决这个问题的关键是如何在 n 的各个数位之间插入加号。显然,我们可以用深度优先搜索(DFS)来枚举所有可能的插入方式(+可放的位置)。

首先,我们将 n 转换为字符串,方便我们处理每一位数;然后我们定义一个全局变量(向量)p 来存储所有 + 的位置。具体来说,如果 + 在第 i 位和第 i+1 位之间,那么我们就在向量 p 中添加一个 i (留在后续不同残缺块相加时使用)。

然后我们定义一个 dfs 函数来实现深度优先搜索。这个函数接收一个参数 u,表示当前正在处理的数位。在这个函数中,我们首先检查 p 中 + 的数量是否超过了 k,或者剩下的数位不足以添加剩余的 +。如果满足以上两种情况之一,我们都停止搜索。

接下来,我们尝试在当前数位 u 的后面插入一个加号。具体说,我们将 u 添加到 p 中,然后递归地调用 dfs(u + 1)。在递归调用返回后,我们需要把 u 从 p 中移出,恢复到原来的状态。

最后,我们还需要尝试不在当前数位的后面插入加号,即直接调用 dfs(u + 1)

dfs 函数的每一次递归调用中,我们都会得到一种插入加号的方式。为了计算这种方式得到的整数和,我们定义一个 update 函数。该函数遍历字符串中的每一位,将其累加到一个临时变量 s 中。每当遍历到 p 中的一个 + 位置时,就将 s 加到结果中,然后将 s 重置为 0。这样我们就得到了一个整数和,然后可以更新最大值和最小值。

由于我们需要枚举字符串中的每一位是否添加 + ,所以时间复杂度是O(2^{len(n)}),其中 len(n) 表示 n 的长度。然而,由于我们可以在 dfs 函数中提前剪枝,实际的运行时间会小于这个上界。

AC_Code

#include <iostream>
#include <cstring>
#include <climits> //LONG_LONG_MAX/MIN
#include <vector>

using namespace std;

string str;
typedef long long LL; //自定义防止溢出
vector<int> p; //定义向量存储+的位置
int n, k;
LL mx = LONG_LONG_MIN, mn = LONG_LONG_MAX;

void update(){
  //残缺块相加、临时变量、向量p中+的位置p=[x1, x2, ..., n]
  LL res = 0, s = 0, t = 0;
  for(int i = 0; i < str.size(); i++){

    //1、12...
    s = s*10 + str[i] - '0';

    //位置i有加号,所有残缺块相加,更新s(下一数字从个位开始),寻找下一个有+的位置
    if(i == p[t])
      res += s, s = 0, t++;
  }
  mx = max(mx, res), mn = min(mn, res);
}

void dfs(int u){

  //失败出口(+多于指定数、剩余可放位置不够放置+)
  if(p.size() > k || p.size() + n - u < k)
    return;
  
  //成功出口(找到+的位置)
  if(u == n){
    p.push_back(u);
    update();
    p.pop_back();
    return;
  }

  //向下搜索
  dfs(u + 1);
  p.push_back(u);
  dfs(u + 1);
  p.pop_back();
}

int main(){
  cin >> str >> k;
  n = str.size() - 1;

  //深搜寻找+的所有位置
  dfs(0);

  cout << mx - mn << endl;
  return 0;
}

示例流程图

总结

首先,通过深搜寻找 + 所有可能的位置。比如 n = 1234,k = 1,那么从dfs(0)开始,一个 + 就可能分别出现在向量 p 的 0/1/2 三个索引位,当 + 放置在索引 0 时,成功出口来调用 update 函数求解最大、最小值,并且计算差值。特别地,update 函数首先会在向量 p 的尾部放入数位的最后一位(在示例里是3),其作用是后分割点,当进入该函数时,假如向量 p = [0, 3],循环遍历 0~3,分割点 res += s,s = 0,t++、非分割点存放在临时变量 s ,第一个分割点 0(索引) 将 1 分割暂存在残缺块res,后续依据临时变量 s 进行数字累加,直到分割点 3(索引)切割,实现 1 + 234 = 235 求得这一次的开心程度值。以此类推。

  • 38
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值