原题来自力扣第964题
这里复制粘贴了原题:
// 给定一个正整数 x,我们将会写出一个表达式,其中每个运算符可以是加、减、乘、除
//(+,-,*,或是/)之一。
// 必须遵循以下规则:
// 除运算符(/)返回有理数。
// 任何地方都没有括号。
// 我们使用通常的操作顺序:乘法和除法发生在加法和减法之前。
// 例如 (x=3,target=19) 19=3*3+3*3+3/3; 返回 5
看到只有很少题解,而且题目标注困难,我就莫名想整点活,给c区加一份题解,于是我开始构思
一开始读这道题,大致就是用pow(x,i)计算阶乘,用乘法逼近。
考虑到这道题还可以用减法,那就需要分偏大和偏小来考虑。
余下的步骤只需要对偏移的数进行递归就可以了。
while(pow(x,i)<target) // 计算出阶乘数,作为二叉树的分支
{
++i;
}
如此一来就可以根据值的偏大、偏小、正好相等分类讨论。
这是正好相等的情况,比较简单:
if(pow(x,i)==target)
{
return i-1; // 直接返回阶数减一作为运算符的个数
}
对于偏大 的情况,考虑到数据的溢出,我反手一个long long,算是打不过就加入:
这里也附上对递归数据的调整,由于while循环的特性,这里取 i 和 i-1 作为指数调整。
如果数据过于偏大,防止陷入死循环,这里需要舍弃对较大值的递归,只计算较小那一支,舍弃的条件就是当调整之后的较大值 m 还要大于 原数据。
long long m=pow(x,i)-target; // 防止数据越界
long long n=target-pow(x,i-1); // 较小分支
if(m>=target) // 毕竟 n 不会超,那就讨论 m 吧
{
res=i-1+leastOpsExpressTarget(x,n); // 超了就只递归 n
}
要是都符合递归的条件,那就顺理成章递归了
else
{
// 合理的二叉递归,往后运算
res=fmin(i+leastOpsExpressTarget(x,m),i-1+leastOpsExpressTarget(x,n));
}
各种数据都递归完毕了,需要讨论当 x<target 的情况了
要是严格等于的话:直接返回零就好了,不需要运算符(加号计算附带在递归中)
要是严格小于,则需要对区间[0,x]进行二分讨论,如下:
f(target<x)
{
// 从 x 中间分段,左端用 1 累加,右端用 x- 若干个 1
if ((2 * target - 1)<(2 * (x - target)))
{
return 2 * target - 1;
}
else
{
return 2 * (x - target);
}
}
到这里也就完成所有的思路以及细节的推导,算是对我整个做题过程的回顾这里附上完整的代码
如有错误,望大佬们指正。
int leastOpsExpressTarget(int x, int target)
{
int res=0;
if(target<x)
{
if ((2 * target - 1)<(2 * (x - target))) // 从 x 中间分段,左端用 1 累加,右端用 x- 若干个 1
{
return 2 * target - 1;
}
else
{
return 2 * (x - target);
}
}
else if(target==x) // 直接返回那一个数,无需运算符
{
return 0;
}
else
{
int i=1;
while(pow(x,i)<target) // 计算出阶乘数,作为二叉树的分支
{
++i;
if(pow(x,i)==target)
{
return i-1;
}
}
long long m=pow(x,i)-target; // 防止数据越界
long long n=target-pow(x,i-1); // 较小分支
if(m>=target) // 毕竟 n 不会超,那就讨论 m 吧
{
res=i-1+leastOpsExpressTarget(x,n); // 超了就只递归 n
}
else
{
res=fmin(i+leastOpsExpressTarget(x,m),i-1+leastOpsExpressTarget(x,n)); // 合理的二叉递归,往后运算
}
}
return res;
}
这里附加一下提交记录:
也是第一次在困难题拿到双百AC的记录,比较激动