我一直在研究Hackerearth问题。问题陈述如下:
我们有三个变量a,b和c。我们需要将a转换为b并允许以下操作:
1.可以递减1.
2.可以递减2.
3.可以乘以c。
将a转换为b所需的最少步骤。
这是我想出的算法:
增量计数为0。
循环直到a === b:
1.执行(x = a * c),(y = a-1)和(z = a-2)。
2.在x,y和z中,选择与b的绝对差最小的那个。
3.将a的值更新为x,y和z中选择的值。
4.将计数增加1。
我可以通过基本测试用例,但是我所有的高级用例都没有通过。我猜我的逻辑是正确的,但是由于复杂性,它似乎失败了。
有人可以提出更优化的解决方案吗?
编辑1
示例代码
function findMinStep(arr) {
let a = parseInt(arr[0]);
let b = parseInt(arr[1]);
let c = parseInt(arr[2]);
let numOfSteps = 0;
while(a !== b) {
let multiply = Math.abs(b - (a * c));
let decrement = Math.abs(b - (a - 1));
let doubleDecrement = Math.abs(b - (a - 2));
let abs = Math.min(multiply, decrement, doubleDecrement);
if(abs === multiply) a = a * c;
else if(abs === decrement) a -= 1;
else a -= 2;
numOfSteps += 1;
}
return numOfSteps.toString()
}
示例输入: a = 3,b = 10,c = 2
说明:将3与2相乘得到6,从6中减去1得到5,将5与2相乘得到10。
同时标记Python和JS的原因:两者都适合,但我不是在寻找代码,只是一种优化的算法和分析思维。
编辑2:
function findMinStep(arr) {
let a = parseInt(arr[0]);
let b = parseInt(arr[1]);
let c = parseInt(arr[2]);
let depth = 0;
let queue = [a, 'flag'];
if(a === b ) return 0
if(a > b) {
let output = Math.floor((a - b) / 2);
if((a - b) % 2) return output + 1;
return output
}
while(true) {
let current = queue.shift();
if(current === 'flag') {
depth += 1;
queue.push('flag');
continue;
}
let multiple = current * c;
let decrement = current - 1;
let doubleDecrement = current -2;
if (multiple !== b) queue.push(multiple);
else return depth + 1
if (decrement !== b) queue.push(decrement);
else return depth + 1
if (doubleDecrement !== b) queue.push(doubleDecrement);
else return depth + 1
}
}
仍然超时。还有其他建议吗?
Link供您参考的问题。
答案 0 :(得分:0)
贪婪的方法在这里行不通。
但是它已经在正确的轨道上了。考虑图G
,其中每个节点代表一个值,每个边沿代表一个操作,并连接与该操作相关的两个值(例如:4和3通过“减1”连接)。使用此图,我们可以轻松地执行BFS-search来找到最短路径:
def a_to_b(a, b, c):
visited = set()
state = {a}
depth = 0
while b not in state:
visited |= state
state = {v - 1 for v in state if v - 1 not in visited} | \
{v - 2 for v in state if v - 2 not in visited} | \
{v * c for v in state if v * c not in visited}
depth += 1
return 1
此查询通过逐步测试来系统地测试所有可能的操作组合,直到达到b
。即从a
生成一个操作可以达到的所有值,然后测试两个操作等可以达到的所有值,直到b
在生成的值之中。
(假设c >= 0
,但可以一概而论)
到目前为止,对于很少进行分析的标准方法。这种方法的优点是它可以解决任何此类问题,并且易于实现。但是,它的效率不是很高,一旦数字增长,它将很快达到极限。因此,我将展示一种方法来深入分析问题并获得(更多)性能更高的解决方案:
第一步,此答案将分析问题:
我们需要进行-->op
操作,以使a -->op b
和-->op
是
c
首先,如果我们先相减然后相乘会怎样?
(a - x) * c = a * c - x * c
接下来,如果我们先相乘然后相减,会发生什么?
a * c - x'
位置系统
嗯,对此没有简化的转换。但是,我们已经掌握了分析更复杂的操作链的基础知识。让我们看看当我们交替链接减法和乘法时会发生什么:
(((a - x) * c - x') * c - x'') * c - x'''=
((a * c - x * c - x') * c - x'') * c - x''' =
(a * c^2 - x * c^2 - x' * c - x'') * c - x''' =
a * c^3 - x * c^3 - x' * c^2 - x'' * c - x'''
您熟悉吗?我们距离以positional system为基础的a
定义b
和c
之间的差异只有一步之遥:
a * c^3 - x * c^3 - x' * c^2 - x'' * c - x''' = b
x * c^3 + x' * c^2 + x'' * c + x''' = a * c^3 - b
不幸的是,以上仍然不是我们所需要的。我们所能知道的是,方程的LHS始终为>=0
。通常,我们首先需要导出适当的指数n
(在上面的示例中为3)s.t。它是最小的,非负的和a * c^n - b >= 0
。对于所有系数均为非负数的单个系数(x
,x'
,...),解决这个问题是一件很琐碎的事情。
我们可以从上面展示两件事:
a < b
和a < 0
没有解决办法最优性证明
可以通过对n
的归纳来证明上面的第二个语句。
n = 0
:在这种情况下为a - b < c
,因此只有一个-->op
n + 1
:让d = a * c^(n + 1) - b
。让d' = d - m * c^(n + 1)
(其中选择了m
)使d'
最小且为非负数。每个归纳假设d'
可以通过位置系统最佳地生成。相差m * c^n
。通过低阶项无法比通过m / 2
减法更有效地弥补这种差异。
算法(TLDR部分)
将a * c^n - b
视为数字基数c
,并尝试查找数字。最终数字应包含n + 1
个数字,其中每个数字代表一定数量的减法。多个减法通过将减法值相加来用一位数字表示。例如。 5表示-2 -2 -1
。从最高有效数字到最低有效数字,该算法的操作如下:
c
,然后从1开始重复下一位数字例如:
a = 3,b = 10,c = 2
选择n = 2
a * c ^ n-b = 3 * 4-10 = 2
二进制2是010 执行的步骤:3-0 = 3,3 * 2 = 6,6-1 = 5,5 * 2 = 10
或
a = 2,b = 25,c = 6
选择n = 2
a * c ^ n-b = 47
47底数6是115
执行的步骤:2-1 = 1,1 * 6 = 6,6-1 = 5,5 * 6 = 30,30-2-2-1 = 25
在python中:
def a_to_b(a, b, c):
# calculate n
n = 0
pow_c = 1
while a * pow_c - b < 0:
n += 1
pow_c *= 1
# calculate coefficients
d = a * pow_c - b
coeff = []
for i in range(0, n + 1):
coeff.append(d // pow_c) # calculate x and append to terms
d %= pow_c # remainder after eliminating ith term
pow_c //= c
# sum up subtractions and multiplications as defined by the coefficients
return n + sum(c // 2 + c % 2 for c in coeff)