【Lintcode】1322. Product Equal B

記錄演算法發表於2021-01-03

題目地址:

https://www.lintcode.com/problem/product-equal-b/description

給定一個長 n n n陣列 A A A,再給定一個數 b b b,每次可以花費 1 1 1的代價將序列中的某個數加 1 1 1或者減 1 1 1。問使得整個 A A A的乘積成為 b b b的最小代價。

思路是動態規劃。先求出 b b b的所有因子,設為其為陣列 f f f,即 f [ i ] f[i] f[i] b b b的從小到大的第 i + 1 i+1 i+1個因子。設 g [ k ] [ j ] g[k][j] g[k][j]是調整 A [ 0 : k ] A[0:k] A[0:k]這個子陣列使得 ∏ A [ 0 : k ] = f [ j ] \prod A[0:k]=f[j] A[0:k]=f[j]的最小代價。若 s s s b b b的因子,設其座標是 m [ s ] m[s] m[s]。我們列舉 A [ k ] A[k] A[k]變成 s s s的情況, s s s b b b的所有因子。則有: g [ k ] [ j ] = min ⁡ s ∣ b { g [ k − 1 ] [ m [ f [ j ] / s ] ] + ∣ A [ k ] − s ∣ } g[k][j]=\min_{s|b} \{g[k-1][m[f[j]/s]]+|A[k]-s|\} g[k][j]=sbmin{g[k1][m[f[j]/s]]+A[k]s}上式的含義是,我們考慮如果要將 A [ 0 : k ] A[0:k] A[0:k]的乘積變為 f [ j ] f[j] f[j]的最小代價。如果最後一個數要變成 s s s,則先要的代價是 ∣ A [ k ] − s ∣ |A[k]-s| A[k]s,此外要讓 A [ 0 : k − 1 ] A[0:k-1] A[0:k1]的乘積變為 f [ j ] / s f[j]/s f[j]/s,當然 f [ j ] / s f[j]/s f[j]/s也是 b b b的因子,其在 f f f中的下標是 m [ f [ j ] / s ] m[f[j]/s] m[f[j]/s],所以調整 A [ 0 : k − 1 ] A[0:k-1] A[0:k1]的乘積變為 f [ j ] / s f[j]/s f[j]/s的最小代價是 g [ k − 1 ] [ m [ f [ j ] / s ] ] g[k-1][m[f[j]/s]] g[k1][m[f[j]/s]] s s s取遍所有 b b b的因子就得到了 g [ k ] [ j ] g[k][j] g[k][j]。初始條件 g [ 0 ] [ j ] = ∣ A [ 0 ] − f [ j ] ∣ g[0][j]=|A[0]-f[j]| g[0][j]=A[0]f[j]。程式碼如下:

import java.util.*;

public class Solution {
    /**
     * @param B: the all Ai product equal to B
     * @param A: the positive int array
     * @return: return the minium cost
     */
    public int getMinCost(int B, int[] A) {
        // write your code here
        List<Integer> fac = new ArrayList<>();
        Map<Integer, Integer> map = new HashMap<>();
        // 求B的所有因子,存入fac列表,並用map記錄每個因子在fac中的下標
        for (int i = 1; i <= B; i++) {
            if (B % i == 0) {
                fac.add(i);
                map.put(i, fac.size() - 1);
            }
        }
    
        int n = fac.size();
        
        // dp[k][i]是調整A[0 : k]使得整個乘積是fac[i]的最小代價
        int[][] dp = new int[A.length][n];
        for (int i = 0; i < dp[0].length; i++) {
            dp[0][i] = Math.abs(A[0] - fac.get(i));
        }
    
        for (int i = 1; i < A.length; i++) {
        	// 先初始化為正無窮
            Arrays.fill(dp[i], Integer.MAX_VALUE);
            // 列舉A[0 : i]的乘積是fac[j]
            for (int j = 0; j < n; j++) {
                // 列舉最後一個數調整為fac[k]
                for (int k = 0; k <= j; k++) {
                    if (fac.get(j) % fac.get(k) == 0) {
                        dp[i][j] = Math.min(dp[i][j], dp[i - 1][map.get(fac.get(j) / fac.get(k))] + Math.abs(A[i] - fac.get(k)));
                    }
                }
            }
        }
        
        return dp[A.length - 1][n - 1];
    }
}

時間複雜度 O ( b + n k 2 ) O(b+nk^2) O(b+nk2) k k k b b b的因子個數,空間 O ( n k ) O(nk) O(nk)

可以用滾動陣列優化。程式碼如下:

import java.util.*;

public class Solution {
    /**
     * @param B: the all Ai product equal to B
     * @param A: the positive int array
     * @return: return the minium cost
     */
    public int getMinCost(int B, int[] A) {
        // write your code here
        List<Integer> fac = new ArrayList<>();
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 1; i <= B; i++) {
            if (B % i == 0) {
                fac.add(i);
                map.put(i, fac.size() - 1);
            }
        }
    
        int n = fac.size();
        int[][] dp = new int[2][n];
        for (int i = 0; i < dp[0].length; i++) {
            dp[0][i] = Math.abs(A[0] - fac.get(i));
        }
    
        for (int i = 1; i < A.length; i++) {
            Arrays.fill(dp[i & 1], Integer.MAX_VALUE);
            for (int j = 0; j < n; j++) {
                for (int k = 0; k <= j; k++) {
                    if (fac.get(j) % fac.get(k) == 0) {
                        dp[i & 1][j] = Math.min(dp[i & 1][j], dp[i - 1 & 1][map.get(fac.get(j) / fac.get(k))] + Math.abs(A[i] - fac.get(k)));
                    }
                }
            }
        }
        
        return dp[A.length - 1 & 1][n - 1];
    }
}

時間複雜度不變,空間 O ( n ) O(n) O(n)

相關文章