一、Problem
給定一個正整數 x,我們將會寫出一個形如 x (op1) x (op2) x (op3) x … 的表達式,其中每個運算符 op1,op2,… 可以是加、減、乘、除(+,-,*,或是 /)之一。例如,對於 x = 3,我們可以寫出表達式 3 * 3 / 3 + 3 - 3,該式的值爲 3
在寫這樣的表達式時,我們需要遵守下面的慣例:
- 除運算符(/)返回有理數。
- 任何地方都沒有括號。
- 我們使用通常的操作順序:乘法和除法發生在加法和減法之前。
- 不允許使用一元否定運算符(-)。例如,“x - x” 是一個有效的表達式,因爲它只使用減法,但是 “-x + x” 不是,因爲它使用了否定運算符。
我們希望編寫一個能使表達式等於給定的目標值 target 且運算符最少的表達式。返回所用運算符的最少數量。
輸入:x = 3, target = 19
輸出:5
解釋:3 * 3 + 3 * 3 + 3 / 3 。表達式包含 5 個運算符。
提示:
2 <= x <= 100
1 <= target <= 2 * 10^8
二、Solution
方法一:記憶化搜索
思路
答案一定有解:因爲我們可以使用 target 個 x/x 相加:x/x + x/x + … + x/x,一個比較聰明的方案是先利用乘法 × 快速地將數值接近 target,然後就是討論邊界啦:
s = tar
,返回結果s > tar
,明顯需要用減法將 s 遞減,s -= x/x + … + x/xs < tar
,s += x/x + x/x + … x/x 或者 s += x - (x/x + … + x/x)
接下來就是定義記憶化搜索的狀態了:
- 定義狀態:
- :即 dfs(cur) 表示湊夠數值 cur 所需要的最少運算符
- 思考初始化:
- 思考狀態轉移方程:
- ,根據上面的情況寫代碼即可
- 思考輸出: 因是記憶化搜索是自底向上的
class Solution {
public:
unordered_map<long, long> f;
long dfs(int x, int re) {
if (re < x) return min(2*re-1, 2*(x-re)); //剩下的re比x還要小,那麼直接用re個x/x或者用x減去(x-re)個 x/x
if (re == 0) return 0;
if (f[re] != 0) return f[re];
long p = log(re)/log(x), sum = pow(x, p), t = sum * x - re;
long ans = dfs(x, re - sum) + p;
if (t < re)
ans = min(ans, p+1 + dfs(x, t));
return f[re] = ans;
}
int leastOpsExpressTarget(int x, int target) {
return dfs(x, target);
}
};
這裏關於爲什麼 sum * x - re < re
就嚴格代表 sum > re
有待證明,會的人歡迎給出證明
複雜度分析
- 時間複雜度:,
- 空間複雜度:,