题目:https://www.nowcoder.com/questionTerminal/b8bc8459f0d34aaa8c1af1328cab2432
算是0-1背包问题的升级版本,用动态规划算法来解决。
方法一:
val[k][i][j]代表对前k个item来说(共x个),当0和1的最大数量分别为i和j时,最多能创造的01串的种类。
状态转移方程:val[k][i][j] = max(val[k-1][i][j], 1 + val[k-1][i-w0][j-w1])
def creatNewWorld():
firstline = input()
# nums = [int(n) for n in firstline.split()]
# x, n, m = nums[0], nums[1], nums[2]
x, n, m = [int(n) for n in firstline.split()]
items = []
for i in range(x):
item = str(input())
items.append(item)
#print (x,n,m,items)
val = [[[None for i in range(m+1)] for j in range(n+1)] for k in range(x+1)]
#print(val[0][0][0]+1)
for i in range(n + 1):
for j in range(m + 1):
val[0][i][j] = 0
for k in range(1,x+1):
w0, w1 = count0and1(items[k-1])
for i in range(n+1):
for j in range(m+1):
if w0 > i or w1 > j:
val[k][i][j] = val[k-1][i][j]
else:
val[k][i][j] = max(val[k-1][i][j], 1 + val[k-1][i-w0][j-w1])
print(val[x][n][m])
def count0and1(item):
c1 = 0
for n in item:
if int(n) == 1:
c1 += 1
return len(item) - c1, c1
creatNewWorld()
结果:空间超了。
改进的方法二:
三维数组太耗时间。由状态转移方程:val[k][i][j] = max(val[k-1][i][j], 1 + val[k-1][i-w0][j-w1])可以看到,val[k][i][j] 只和val[k-1][...][...]有关,所以可以压缩成二维数组来存状态。
注意:这里在循环的时候,i和j要逆序地循环,这是因为val[i][j] = max(val[i][j], 1 + val[ i-zeros[k-1] ][ j-ones[k-1] ]),i和j较大的val的计算基于i和j较小的val的值,如果i和j从小到大循环,就重复计数了。
def creatNewWorld():
firstline = input()
x, n, m = [int(n) for n in firstline.split()]
zeros = [ 0 for i in range(x)]
ones = [ 0 for i in range(x)]
for i in range(x):
item = str(input())
zeros[i], ones[i] = count0and1(item)
val = [[0 for i in range(m + 1)] for j in range(n + 1)]
for k in range(1,x+1):
for i in range(n, zeros[k-1] - 1, -1):
for j in range(m, ones[k-1] - 1, -1):
val[i][j] = max(val[i][j], 1 + val[ i-zeros[k-1] ][ j-ones[k-1] ])
print(val[n][m])
def count0and1(item):
c1 = 0
for n in item:
if int(n) == 1:
c1 += 1
return len(item) - c1, c1
creatNewWorld()
但是依然超时,70%case能成功。进过慎重地排除,我觉得是牛客网的oj系统造成的,本身代码没问题。确切地说也不是牛客网的问题,牛客网无论什么题目什么语言就限时1s,同样地算法用java写一遍可以通过,改成python就超时了,毕竟python效率不够高。我在评论区也找到一个用python的也是这种情况,总是到70%case就超时。代码我自己用pycharm测试了好多次,是没问题的。
扩展问题:
这个题目让求的是【最多能创造几种01串】,不过在一开始很容易让人看错成【最多能创造几个01串】。所以思考一下扩展问题【最多能创造几个01串】呢?
原问题相当于一种01串只能创造一次,拓展问题相当于一种01串可以创造多次。
只要改一个地方就可以了——那就是方法二中强调了i和j要逆序循环,在这个问题中,改成正序循环就可以了。