DFS深搜知识回顾:深度优先搜索(DFS)之飞机降落-CSDN博客
问题描述
人类的开心程度有高低之分,数字也一样。
给定一个正整数 n,在 n 的数位之间插入 k 个加号,使其变成一个表达式,计算得出的结果就是 n 的一个 k 级开心程度。
例如 n=1234,k=1 时,我们可以往 2 和 3 之间插入一个 + 号,使其变为 12+34,计算出结果为 46 。那么 46 就是 1234 的一个 k 级开心程度。
给定 n,k 请你计算出 n 的 k 级开心程度的最大值与最小值之差。
输入格式
一行输入两个正整数 n,k,含义见题面。
输出格式
一行一个整数,表示 n 的 k 级开心程度的最大值与最小值之差。
样例输入
1234 1
输出样例
189
说明
可以证明 1234 的 1 级开心程度的最大值为 1+234=235,最小值为 12+34=46,故最大值与最小值的差值为 189。
数据范围
对于 20% 的测试样例,1≤n≤,k=1。
对于 100% 的测试样例,1≤n≤,1≤k<len(n),其中 len(x) 表示 x 的长度。
运行限制
语言 | 最大运行时间 | 最大运行内存 |
---|---|---|
C++ | 1s | 512M |
C | 1s | 512M |
Java | 2s | 512M |
Python3 | 3s | 512M |
思路
根据题目举例。给定一个正整数和一个整数 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 转换为字符串,方便我们处理每一位数;然后我们定义一个全局变量(向量) 来存储所有
的位置。具体来说,如果
在第 i 位和第 i+1 位之间,那么我们就在向量
中添加一个 i (留在后续不同残缺块相加时使用)。
然后我们定义一个 dfs 函数来实现深度优先搜索。这个函数接收一个参数 u,表示当前正在处理的数位。在这个函数中,我们首先检查 中
的数量是否超过了 k,或者剩下的数位不足以添加剩余的
。如果满足以上两种情况之一,我们都停止搜索。
接下来,我们尝试在当前数位 u 的后面插入一个加号。具体说,我们将 u 添加到 中,然后递归地调用 dfs(u + 1)。在递归调用返回后,我们需要把 u 从
中移出,恢复到原来的状态。
最后,我们还需要尝试不在当前数位的后面插入加号,即直接调用 dfs(u + 1)。
在 dfs 函数的每一次递归调用中,我们都会得到一种插入加号的方式。为了计算这种方式得到的整数和,我们定义一个 update 函数。该函数遍历字符串中的每一位,将其累加到一个临时变量 s 中。每当遍历到 中的一个
位置时,就将 s 加到结果中,然后将 s 重置为 0。这样我们就得到了一个整数和,然后可以更新最大值和最小值。
由于我们需要枚举字符串中的每一位是否添加 ,所以时间复杂度是
,其中 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)开始,一个
就可能分别出现在向量
的 0/1/2 三个索引位,当
放置在索引 0 时,成功出口来调用 update 函数求解最大、最小值,并且计算差值。特别地,update 函数首先会在向量
的尾部放入数位的最后一位(在示例里是3),其作用是后分割点,当进入该函数时,假如向量
= [0, 3],循环遍历 0~3,分割点 res += s,s = 0,t++、非分割点存放在临时变量 s ,第一个分割点 0(索引) 将 1 分割暂存在残缺块res,后续依据临时变量 s 进行数字累加,直到分割点 3(索引)切割,实现 1 + 234 = 235 求得这一次的开心程度值。以此类推。