HJ41_称砝码_动态规划_双层循环的内层循环对象同时更新(巧妙)

发布时间 2023-03-28 10:45:07作者: Aneverforget

思路:陈砝码也就是砝码有多少种组合方式。
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))