HJ41_称砝码_动态规划_双层循环的内层循环对象同时更新(巧妙)
思路:陈砝码也就是砝码有多少种组合方式。
1.用穷举方法,但是操作量大,且同一重量可以有多重不同砝码称取方式。
2.用确定砝码称取范围(0,max_weight),并逆推组合是否成立的方式,可减少计算量。
这个方法还不知如何实现。
如实现方式为每次取最接近重量的砝码,砝码有2g两个,3g一个,称重4g.计算取不到该重量。
如果接下去按次大重量组合,这种计算实现与第一的穷举计算量一样大,但且麻烦。
想到一个好案例再设计计算方法就有个好依托。这样设计起来更形象了。
!!!
3.采用动态规划参考背包问题,但与背包动态规划有所不同。
背包动态规划的状态方程为,第n个物品装包的价值和第n个物品不装包的价值比较取最大值。
dp[i][j] = max(dp[i−1][j], dp[i−1][j−w[i]]+v[i]) // j >= w[i]
而称砝码的动态规划最终输出可实现可能重量的列表(i为第i个砝码,j为可称重量)
i为矩阵横,j为矩阵列:实现如下。
1 #运算时间2001ms,内存5548kb 2 import copy 3 a=int(input()) 4 ma=list(map(int,input().split())) 5 b=list(map(int,input().split())) 6 #ma=list(map(int,input().split())) 7 #b=list(map(int,input().split())) 8 w=[] 9 for i in range(len(b)):#列出所有砝码重量 10 w=w+[ma[i]]*b[i] 11 n=0 12 for i in range(len(b)):#求出砝码最大可称取重量 13 n=ma[i]*b[i]+n 14 dp=[[0 for i in range(n+1)] for j in range(len(w))] 15 dp[0][w[0]]=w[0]#填好矩阵第一行 16 for i in range(1,len(w)):#Bfs#矩阵列坐标 17 dp[i]=copy.deepcopy(dp[i-1])#dp[i]=dp[i-1]不是拷贝造成错误,把所有可能称取的重量放入下一行 18 dp[i][w[i]]=w[i] 19 for j in range(n+1):#矩阵行坐标 #新的砝码加入,添加新的可能称取的重量 20 if j-w[i] in set(dp[i-1]):#填好矩阵其他行 21 #print(set(dp[i-1]),set(dp[i]),j) 22 dp[i][j]=j 23 #print(set(dp[i-1]),i,j) 24 #print(set(dp[i]),set(dp[i-1]),j,w[i],i) 25 temp=set(dp[len(w)-1]) 26 #print(temp) 27 out=len(temp) 28 print(out)#2为最大数和0的重量
优化1
1 #优化 dp变为一维 2 #您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。 3 #运算时间2001ms,内存5012kb 4 import copy 5 a=int(input()) 6 ma=list(map(int,input().split())) 7 b=list(map(int,input().split())) 8 w=[] 9 for i in range(len(b)):#列出所有砝码重量 10 w=w+[ma[i]]*b[i] 11 n=0 12 for i in range(len(b)):#求出砝码最大可称取重量 13 n=ma[i]*b[i]+n 14 dp=[0 for i in range(n+1)]#dp缩短为一行 15 for i in w: 16 tem=copy.deepcopy(dp) 17 dp[i]=i 18 for j in range(n+1): 19 if j-i in set(tem): 20 dp[j]=j 21 print(set(dp),set(tem),i) 22 temp=set(dp) 23 #print(temp) 24 out=len(temp) 25 print(out)#2为最大数和0的重量
优化2
1 #优化 dp类型变为集合 2 #运算时间2001ms,占用内存5288kb 3 import copy 4 a=int(input()) 5 ma=list(map(int,input().split())) 6 b=list(map(int,input().split())) 7 #16601 8 a=10 9 ma=[2000,1999,1998,1997,1996,1995,1994,1993,1992, 10 1991] 11 b=[10]*10 12 w=[] 13 for i in range(len(b)):#列出所有砝码重量 14 w=w+[ma[i]]*b[i] 15 n=0 16 for i in range(len(b)):#求出砝码最大可称取重量 17 n=ma[i]*b[i]+n 18 dp={0} 19 for i in w: 20 tem=copy.deepcopy(dp) 21 dp.add(i) 22 for j in range(n+1):# 23 if j-i in tem:#填好矩阵其他行 24 dp.add(j) 25 #print(set(dp),set(tem),i) 26 out=len(dp) 27 print(out)#2为最大数和0的重量
优化3.通过
巧妙的双层循环:
双层循环中同时更新内层循环列表
#运算时间ms,占用内存kb
#参考高赞题解思路:
#1、把所有砝码及个数整合成一个列表w
#2、砝码循环与weights列表相加。weights列表为可称取重量。
#没加入一个砝码可称取重量列表就发生一次改变。
#思路与我上述相同,但是代码更简洁,循环计算数量更少。
#总思路是,砝码每增加一个,可称取重量发生一次改变。在代码实现。
1 a=int(input()) 2 ma=list(map(int,input().split())) 3 b=list(map(int,input().split())) 4 w=[] 5 weights={0} 6 for i in range(len(b)): 7 w=w+[ma[i]]*b[i] 8 for i in w: 9 for j in list(weights):#要把set转换成list进入循环,否则报错 10 # RuntimeError: Set changed size during iteration 11 weights.add(i+j) 12 print(len(weights))