DP:解决贪心法的不足
DP基础介绍:
一维情况(数硬币问题):
只有1元硬币
有1元,5元硬币
def solve(s): # s为金额数
dp = [int(1e12)]*(s+1) #初始化为无穷大
dp[0] = 0
for j in range (cnt): # cnt种硬币
for i in range(type[j],s+1) : # 从新增的硬币值类型开始循环
dp[i] = min(dp[i], dp[i - type[j]] +l) # 状态转移方程
print(dp[s])
cnt = 5 # 5种硬币
type = [1,5,10,25,50] #5种面值
s = int(input())
solve(s)
二维情况(0/1背包问题)
状态转移:比较两种不同选择的最大价值
倒推查找装了哪些物品,如果装了新物品,价值会变,否则继承上一个价值
经典01背包问题
找到状态转移方程,切记对于dp数组,价值数组,体积数组给他大空间初始化,下标从1开始,重点在于状态转移方程
def solve(N,C): # 从左到右,从上到下 (先种类,再体积)
for i in range(1,N+1): # N种物品,先1种,再2种......
for j in range(0,C+1): # 当前背包体积
if c[i]>j : dp[i][j] = dp[i-1][j]
else: dp[i][j] = max(dp[i-1][j-c[i]]+w[i],dp[i-1][j]) # 装或者不装,找最大值
return dp[N][C]
N,C= map(int,input().split())
n=3000
dp = [[0]*n for i in range(n)] # 初始化dp数组,预留更大空间
c=[0]*n # 记录体积
w=[0]*n # 记录价值
for i in range(1,N+1): #读入N种物品的价值和体积
c[i],w[i] = map(int,input().split())
print(solve(N,C))
01背包问题优化,优化空间大小,只是用两行队列存储
区别对比
def solve(N,C): # 从左到右,从上到下 (先种类,再体积)
old = 1
new = 0
for i in range(1,N+1): # N种物品,先1种,再2种......
new,old = old,new # 交换两行
for j in range(0,C+1): # 当前背包体积
if c[i]>j : dp[new][j] = dp[old][j]
else: dp[new][j] = max(dp[old][j-c[i]]+w[i],dp[old][j]) # 装或者不装,找最大值
return dp[N][C]
N,C= map(int,input().split())
n=3000
dp = [[0]*n for i in range(2)] # 初始化dp数组,预留更大空间
c=[0]*n # 记录体积
w=[0]*n # 记录价值
for i in range(1,N+1): #读入N种物品的价值和体积
c[i],w[i] = map(int,input().split())
print(solve(N,C))
01背包问题再优化,只有1行存储
def solve(N,C): # 从左到右,从上到下 (先种类,再体积)
for i in range(1,N+1): # N种物品,先1种,再2种......
for j in range(C,c[i]-1,-1): # 当前背包体积,倒序进行,到c[i]-1,因为范围[c[i],C]
dp[j] = max(dp[j],dp[j-c[i]]+w[i]) # 选或者不选
return dp[C]
N,C= map(int,input().split())
n=3000
dp = [0]*n # 初始化dp数组,预留更大空间 一维
c=[0]*n # 记录体积
w=[0]*n # 记录价值
for i in range(1,N+1): #读入N种物品的价值和体积,从1开始计数
c[i],w[i] = map(int,input().split())
print(solve(N,C))
01背包简化版
价值相同,最大化体积
一维dp
def solve(N,C): # 从左到右,从上到下 (先种类,再体积)
for i in range(1,N+1): # N种物品,先1种,再2种......
for j in range(C,c[i]-1,-1): # 当前背包体积,倒序进行,到c[i]-1,因为范围[c[i],C]
dp[j] = max(dp[j],dp[j-c[i]]+c[i]) # 选或者不选
return dp[C]
V = int(input()) # 体积
N = int(input()) # 物品种类
n=21000
dp = [0]*n # 初始化dp数组,预留更大空间 一维
c=[0]*n # 记录体积
for i in range(1,N+1): #读入N种物品的价值和体积,从1开始计数
c[i]=int(input())
print(V-solve(N,V))
二维dp
def solve(N,C): # 从左到右,从上到下 (先种类,再体积)
new = 0
old = 1
for i in range(1,N+1): # N种物品,先1种,再2种......
new,old = old,new
for j in range(0,C+1):
if c[i]>j: dp[new][j] = dp[old][j]
else: dp[new][j] = max(dp[old][j-c[i]]+c[i],dp[old][j])
return dp[N][C]
V = int(input()) # 体积
N = int(input()) # 物品种类
n=21000
dp = [[0]*n for i in range(2)] # 初始化dp数组,预留更大空间 一维
c=[0]*n # 记录体积
for i in range(1,N+1): #读入N种物品的价值和体积,从1开始计数
c[i]=int(input())
print(V-solve(N,V))
完全背包问题(装的个数不受限制)
def solve(N,C): # 从左到右,从上到下 (先种类,再体积)
new = 0
old = 1
for i in range(1,N+1): # N种物品,先1种,再2种......
new,old = old,new
for j in range(0,C+1):
# 先欠着(有问题)
for k in range(0,j//c[i]+1):
dp[new][j] = max(dp[old][j],dp[old][j-k*c[i]]+k*w[i])
return dp[N][C]
V = int(input()) # 体积
N,C = map(int,input().split()) # 物品种类
n=21000
dp = [[0]*n for i in range(2)] # 初始化dp数组,预留更大空间 一维
c=[0]*n # 记录体积
w=[0]*n
for i in range(1,N+1): #读入N种物品的价值和体积,从1开始计数
c[i],w[i]=map(int,input().split())
print(solve(N,C))
最长公共子序列,线性DP
n,m = map(int,input().split()) # B n个元素 A m个元素
a = [0] + list(map(int,input().split()))
b = [0] + list(map(int,input().split()))
dp = [[0]*(m+1) for _ in range(2)] # 注意这里是m,不是n
now = 0 ;old = 1
for i in range(1,n+1):
now,old = old,now
for j in range(1,m+1):
dp[now][j] = max(dp[now][j-1],dp[old][j])
if a[i]==b[j]: # 相同的元素
dp[now][j] = max(dp[now][j],dp[old][j-1]+1)
print(dp[now][m])