Medium

Minimum Array SumPython

Full explanation · Time O(nlogn) · Space O(n)

# Time:  O(nlogn)
# Space: O(n)

# greedy, case works
# Reference: https://leetcode.com/problems/minimum-array-sum/solutions/6078002/o-n-log-n-greedy/
class Solution(object):
    def minArraySum(self, nums, k, op1, op2):
        """
        :type nums: List[int]
        :type k: int
        :type op1: int
        :type op2: int
        :rtype: int
        """
        nums.sort()

        left = next((i for i in xrange(len(nums)) if nums[i] >= k), len(nums))
        right = next((i for i in xrange(len(nums)) if nums[i] >= 2*k-1), len(nums))

        lookup, cnt = [False]*len(nums), 0
        for j in reversed(xrange(right, len(nums))):
            if not op1:
                break
            op1 -= 1
            nums[j] = (nums[j]+1)//2
            if op2:
                op2 -= 1
                nums[j] -= k
        else:
            j = right-1

        for i in xrange(left, j+1):
            if not op2:
                break
            op2 -= 1
            if k%2 == 1 and nums[i]%2 == 0:
                lookup[i] = True
            nums[i] -= k
        else:
            i = j+1
    
        for j in reversed(xrange(i, j+1)):
            if not op1:
                break
            op1 -= 1
            if k%2 == 1 and nums[j]%2 == 1:
                cnt += 1
            nums[j] = (nums[j]+1)//2
        else:
            j = i-1

        arr = sorted((nums[idx], idx) for idx in xrange(i))
        for _ in xrange(op1):
            x, idx = arr.pop()
            nums[idx] = (x+1)//2
            if cnt and lookup[idx]:
                cnt -= 1
                nums[idx] -= 1
        return sum(nums)


# Time:  O(n * op1 * op2)
# Space: O(op1 * op2)
# dp
class Solution2(object):
    def minArraySum(self, nums, k, op1, op2):
        """
        :type nums: List[int]
        :type k: int
        :type op1: int
        :type op2: int
        :rtype: int
        """
        dp = [[sum(nums)]*(op2+1) for _ in xrange(op1+1)]
        for x in nums:
            for i in reversed(xrange(op1+1)):
                for j in reversed(xrange(op2+1)):
                    if i-1 >= 0:
                        dp[i][j] = min(dp[i][j], dp[i-1][j]-x+(x+1)//2)
                    if j-1 >= 0:
                        if x-k >= 0:
                            dp[i][j] = min(dp[i][j], dp[i][j-1]-x+(x-k))
                    if i-1 >= 0 and j-1 >= 0:
                        if x-k >= 0:
                            dp[i][j] = min(dp[i][j], dp[i-1][j-1]-x+((x-k)+1)//2)
                        if (x+1)//2-k >= 0:
                            dp[i][j] = min(dp[i][j], dp[i-1][j-1]-x+((x+1)//2-k))
        return dp[op1][op2]