除法除了构成1,是没有作用的。
本题可以转化为,用 x/x,x,x*x,x*x*x,... 构建target。
由于题目中限定target为正数,因此肯定有一项为正,我们可以调整位置是的正项放在最前面。对于每一项,考虑前面的符号,我们可以得到构成这些项所需的符号数。第一项的正号是不需要的,因此最后答案-1即可。
target = c0x^0 + c1x^1 + c2x^2 + ... + cnx^n,其中 | ci | < x,因为如果大于x,可以重写等式。
如果我们每次计算 r = target % x,我们有两种选择,加上 r*对应项的操作,或者减去 (x-r)*对应项的操作。
可以看几个例子 https://leetcode.com/problems/least-operators-to-express-number/solution/
因此递归的时候也需记录当前处理的是哪一项,用i表示。
f(i,target) = min( r*cost(i)+f(i+1,t), (x-r)*cost(i)+f(i+1,t+1) )
递归终止条件比较复杂,首先如果 target=0,返回0
如果 target=1,直接返回cost(i),否则会死循环。
还有就是递推的时候,如果整除了,那我们肯定没必要再 (x-r)*cost(i)+f(i+1,t+1)
bottom up dp 比较困难,直接 top down 加个 memo,效率也会更高一些。
class Solution { public: unordered_map<string,int> memo; int x; int leastOpsExpressTarget(int x, int target) { this->x = x; return f(0,target)-1; } int f(int i, int target){ string encoded=to_string(i)+' '+to_string(target); if (memo.count(encoded)) return memo[encoded]; int res; if (target==0) res=0; else if (target==1){ return cost(i); }else{ int t=target/x; int r=target%x; res = r*cost(i) + f(i+1,t); if (r!=0) res=min(res, (x-r)*cost(i) + f(i+1,t+1)); } return memo[encoded]=res; } int cost(int i){ if (i==0) return 2; // +x/x else return i; } };
时间复杂度 O(logx(target))
一道比较类似的题: