MinAbsSum

这篇博客探讨了如何解决一个计算机科学问题,即寻找数组A中与序列S乘积的绝对值最小的和。通过动态规划的方法,算法需要在给定的数组A(长度N,元素范围[-100, 100])中找到一个由{-1, 1}组成的序列S,使得val(A, S)达到最小。示例给出了一个具体的数组A,并解释了如何通过优化算法避免超时错误。" 44877243,4993817,使用BusyBox的tftp服务进行文件传输,"['嵌入式开发', '网络通信', '文件操作']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://app.codility.com/programmers/lessons/17-dynamic_programming/min_abs_sum/

https://codility.com/media/train/solution-min-abs-sum.pdf

 

For a given array A of N integers and a sequence S of N integers from the set {−1, 1}, we define val(A, S) as follows:

val(A, S) = |sum{ A[i]*S[i] for i = 0..N−1 }|

(Assume that the sum of zero elements equals zero.)

For a given array A, we are looking for such a sequence S that minimizes val(A,S).

Write a function:

class Solution { public int solution(int[] A); }

that, given an array A of N integers, computes the minimum value of val(A,S) from all possible values of val(A,S) for all possible sequences S of N integers from the set {−1, 1}.

For example, given array:

A[0] = 1 A[1] = 5 A[2] = 2 A[3] = -2

your function should return 0, since for S = [−1, 1, −1, 1], val(A, S) = 0, which is the minimum possible value.

Write an efficient algorithm for the following assumptions:

  • N is an integer within the range [0..20,000];
  • each element of array A is an integer within the range [−100..100].

思路:一个比较直观的想法是背包,复杂度N*sum(A),大概20000*20000*100,会TLE

# you can write to stdout for debugging purposes, e.g.
# print("this is a debug message")

def solution(A):
    # write your code in Python 3.6
    a=[abs(s) for s in A]
    su=sum(a)
    ta=su//2
    
    dp=[[0 for _ in range(ta+1)] for _ in range(len(a))]
    for i in range(a[0],ta+1): dp[0][i]=a[0]
    for i in range(1,len(a)):
        for j in range(ta+1):
            dp[i][j]=dp[i-1][j]
            if j>=a[i]:
                dp[i][j]=max(dp[i-1][j], dp[i-1][j-a[i]]+a[i])
    
#    print(su, dp[-1][-1])
    return su-dp[-1][-1] - dp[-1][-1]


print(solution([1,5,2,2]))
print(solution([1,2,100]))

另外一个思路是考虑每来一个元素,update每个数能不能到达,复杂度仍然跟上面一样

这里dp[s]表示到目前为止(看到A[0..i]),s是否能到达

# you can write to stdout for debugging purposes, e.g.
# print("this is a debug message")

def solution(A):
    # write your code in Python 3.6
    a=[abs(s) for s in A]
    su=sum(a)
    
    dp=[False for _ in range(su//2+1)]
    dp[0]=True
    dp2=dp.copy()
    for i in a:
        for p in range(i,su//2+1):
            dp2[p]=dp[p]|dp[p-i]
        for p in range(i,su//2+1):
            dp[p]=dp2[p]
    
    for i in range(su//2,-1,-1):
        if dp[i]: return su-2*i


print(solution([1,5,2,2]))
print(solution([1,5,2,-2]))
print(solution([1,2,100]))

但是注意到A数组的每个数范围比较小,所以有很多重复,可以先对A计数,然后用dp[s]表示:看到i的时候,到s位置时,还有dp[s]个i剩余。

这就相当于把数组A重新排列一下,相同的数排到一起,然后用上面的方法进行DP(因为排在一起,所以可以减小计算量)

# you can write to stdout for debugging purposes, e.g.
# print("this is a debug message")
from collections import Counter

def solution(A):
    # write your code in Python 3.6
    if len(A)==0: return 0
    if len(A)==1: return abs(A[0])
    a=[abs(s) for s in A]
    su=sum(a)
    ma=max(a)
    d=Counter(a)
    
    dp=[-1 for _ in range(su//2+1)]
    dp[0]=0
    for i in range(1,ma+1):
        if i not in d: continue
        for p in range(su//2+1):
            if dp[p]>=0: dp[p]=d[i]  # dp[p]可以到达,完全不需要使用A[i]
            elif p>=i and dp[p-i]>0: dp[p]=dp[p-i]-1 # dp数组虽然经过很多赋值,但是dp[p-i]一定表示到p-i的时候,还剩下dp[p-i]个i

    for i in range(su//2,-1,-1):
        if dp[i]>=0: return su-2*i


print(solution([]))
print(solution([1,5,2,2]))
print(solution([1,5,2,-2]))
print(solution([1,2,100]))

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值