【蓝桥杯-砝码称重】

蓝桥杯-砝码称重

问题描述:
你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1, W2, · · · WN
请你计算一共可以称出多少种不同的重量?
注意砝码可以放在天平两边

输入的第一行包含一个整数 N
第二行包含 N 个整数:W1, W2, W3, · · · WN输出一个整数代表答案
输出一个整数代表答案

样例输入
3
1 4 6
样例输出
10

这篇文章主要是分析动态规划解决此类问题。

不用动态规划,这样的问题也可以解决,比如用枚举法


##枚举法,时间超时
while True:
    try:
        N = int(input())
        W = list(map(int,input().split()))
        nums = W.copy()
        dp = [[0]]
        for i in range(N):
            dp_list = []
            for j in range(len(dp[i])):
                temp_dp1 = dp[i][j]+W[i]*1
                temp_dp2 = abs(dp[i][j]+W[i]*(-1))
                temp_dp3 = dp[i][j]+W[i]*0
                if temp_dp1 not in nums:
                    nums.append(temp_dp1)
                if temp_dp2 not in nums:
                    nums.append(temp_dp2)
                if temp_dp3 not in nums:
                    nums.append(temp_dp3)
                dp_list.extend([temp_dp1,temp_dp2,temp_dp3])
            dp.append(dp_list)
        print(len(nums)-1)
    except:
        break

但这个时间是超时的,因为枚举法消耗资源过多,算法复杂度随着N的增大,呈现指数增长,其中,我列举了3中状态,对于每个砝码他可以选择放在左边、右边、或者不放。

接下来我们来看动态规划解法

N = int(input())
weights = list(map(int,input().split()))
maxlen = sum(weights) #这是天平所能称重的最大范围
dp = [[0]*(maxlen*2+1) for i in range(N+1)]  #多生成一列和一行
for i in range(N+1):
    dp[i][0] = 1
for i in range(1,N+1):
    for j in range(maxlen+1):    #首先初始化最边际地带
        if dp[i-1][j] == 1: #如果前i-1能取到j
            dp[i][j] = 1 ##前i-1个能取到,前i个一定能取到
            dp[i][j+weights[i-1]] = 1
            dp[i][abs(j-weights[i-1])] = 1
print(sum(dp[N])-1) #多一个0

动态规划能解决的问题,有这样的特征,有重复计算值、上一子问题可以作为当前子问题的提取库。

N = int(input())
weights = list(map(int,input().split()))
maxlen = sum(weights) #这是天平所能称重的最大范围
dp = [[0]*(maxlen*2+1) for i in range(N+1)]

一般dp是要定义一个多一行一列的取值库,不过对此问题而言,需要变动一下成二倍列,多一行的取值库,为了防止超出索引。

for i in range(1,N+1):
    for j in range(maxlen+1):    #首先初始化最边际地带
        if dp[i-1][j] == 1: #如果前i-1能取到j
            dp[i][j] = 1 ##前i-1个能取到,前i个一定能取到
            dp[i][j+weights[i-1]] = 1
            dp[i][abs(j-weights[i-1])] = 1
print(sum(dp[N])-1) #多一个

动态规划主要切入点是分析dp[i]与dp[i-1]的关系,有啥关心呢?

  • dp[i][j]代表的是前i+1项能否取到j值,能则为1否者为0,那么dp[i-1][j]为1,dp[i][j]一定为1,这样可以得到关系dp[i-1][j]=dp[i][j],条件前者为1
    根据题意,前面提到-对于每个砝码他可以选择放在左边、右边、或者不放,这咋体现呢?
    放右边边:dp[i][j+weights[i-1]],一开始小白会???????,这怎么回事呢?j+weigths不会超额吗?答案是不会,因为前面条件的限制,dp[x][j=maxlen]会在最后出现。
    放左边:dp[i][abs(j-weigths[i-1])],涉及到减号,加上abs
    不放:过程中已经体现了,不信你频频
    动态规划核心是前i!前i!前i!,啥题目都这样。

你可能感兴趣的:(蓝桥杯,算法,职场和发展)