python.py

scratch pad/python.pyPython108 lines

Read-only. This is working scratch, not reference material — it may not compile, may not be correct, and will change without warning.

from typing import List
from bisect import bisect_left
from collections import defaultdict

class Solution:
    def minArraySum2(self, nums: List[int], k: int, op1: int, op2: int) -> int:
        '''
        Couldn't figure out the corner case on my own, below solution doesn't work!
        Solution using greedy approach that works: https://leetcode.com/problems/minimum-array-sum/solutions/6078002/o-n-log-n-greedy
        '''
        nums.sort()
        largeNums = bisect_left(nums, 2*k-1)
        mediumNums = bisect_left(nums, k)

        i = len(nums)-1
        while op1 and i >= largeNums:
            nums[i] = (nums[i]+1)//2
            op1 -= 1
            if op2:
                nums[i] -= k
                op2 -= 1
            i -= 1

        j = mediumNums
        while op2 and j <= i:
            nums[j] -= k
            op2 -= 1
            j += 1

        nums = sorted(nums[:i+1]) + nums[i+1:]
        while op1 and i > -1:
            if nums[i] < 2: break
            nums[i] = (nums[i]+1) //2
            op1 -= 1
            i -= 1

        return sum(nums)
    
    
    # Solution 2 using dynamic programming
    def minArraySum(self, nums: List[int], k: int, op1: int, op2: int) -> int:
        '''
        Works flawlessly.
        Time Complexity: O(n * op1 * op2)
        Space Complexity: O(n * op1 * op2)
        '''
        memo = dict()

        def apply(i: int, op1: int, op2: int) -> int:
            if i >= len(nums): return 0
            if (i, op1, op2) in memo: return memo[(i, op1, op2)]

            n = nums[i]
            answer = n + apply(i+1, op1, op2)
            if op1: answer = min(answer, (n+1)//2 + apply(i+1, op1-1, op2))
            if op2 and n >= k: answer = min(answer, n-k + apply(i+1, op1, op2-1))
            if op1 and op2:
                if n >= k: answer = min(answer, (n-k+1)//2 + apply(i+1, op1-1, op2-1))
                if n >= 2*k-1: answer = min(answer, (n+1)//2 - k + apply(i+1, op1-1, op2-1))
            memo[(i, op1, op2)] = answer
            return answer

        return apply(0, op1, op2)





if __name__ == "__main__":
    sol = Solution()
    questions = [
        (
            ([1, 3, 5, 7, 9, 12, 12, 12, 13, 15, 15, 15, 16, 17, 19, 20], 11, 15, 4),
            77
        ),
        (
            ([5,5], 1, 1, 2),
            6
        ),
        (
            ([1], 1, 0, 1),
            0
        ),
        (
            ([0,7,0,2,3], 2, 4, 4), 
            2
        ),
        (
            ([2,10,9,0,4], 3, 5, 2), 
            7
        ),
        (
            ([7,4,4,8], 3, 3, 1),
            11
        ),
        (
            ([5], 2, 1, 0), 
            3
        )
    ]

    for question, answer in questions:
        original = list(question[0])
        result = sol.minArraySum(*question)
        if result == answer:
            print(f"Success! {original} -> {result}")
        else:
            print(f"Failed! {original} -> {result} (answer: {answer})")