您的位置:首页 > 其它

动态规划

2016-12-15 14:39 288 查看

动态规划

最大连续子序列和

从O(N^3)到O(N)

问题: 给定一个序列, 求出最大连续的子序列和.

穷举算法 O(N^3)

python code

L = [-2, 11, -4, 13, -5, -2]
N = len(L)
max_sum = L[0]

for begin in range(N):
for end in range(begin, N):
sum = 0
for i in range(begin, end+1):
sum += L[i]
if sum > max_sum:
max_sum = sum

print(max_sum)


递进算法 O(N^2)

python code

L = [-2, 11, -4, 13, -5, -2]
N = len(L)
max_sum = L[0]

for begin in range(N):
sum = 0
for i in range(begin, N):
sum += L[i]
if sum > max_sum:
max_sum = sum

print(max_sum)


分治算法 O(NlogN)

python code

L = [-2, 11, -4, 13, -5, -2]
N = len(L)

def max_sum(low, high):
if high-low == 1:
return L[low]

# get mid between low and high
mid = low + (high-low)//2
# return max sum of 2 parts
maxz = max(max_sum(low, mid), max_sum(mid, high))

# left sum
left_sum = L[mid-1]
sum, i = 0, mid-1
while i >= low:
sum += L[i]
left_sum = max(left_sum, sum)
i -= 1

# right sum
right_sum = L[mid]
sum, i = 0, mid
while i < high:
sum += L[i]
right_sum = max(right_sum, sum)
i += 1

# return max sum
mid_sum = left_sum + right_sum
return max(maxz, mid_sum)

ans = max_sum(0, N)
print(ans)


动态规划 O(N)

状态方程: MaxSum[i]=max(MaxSum[i-1]+L[i], L[i])

python code

L = [-2, 11, -4, 13, -5, -2]
N = len(L)

DP = [0 for i in range(N)]
max_sum = DP[0] = L[0]

for i in range(1, N):
if DP[i-1]+L[i] >= L[i]:
DP[i] = DP[i-1] + L[i]
else:
DP[i] = L[i]
max_sum = max(max_sum, DP[i])

print(max_sum)


贪心算法 O(N)

python code

L = [-2, 11, -4, 13, -5, -2]
N = len(L)

max_sum = L[0]
sum = 0
for i in range(1, N):
sum += L[i]
max_sum = max(max_sum, sum)
if sum < 0:
sum = 0

print(max_sum)


数数字

从递归到递推

问题: 从0开始, 可以加上1, 也可以加上2, 递归此步骤, 一直加到正好是N的方案有多少种?

DFS O(2^N)

直接搜索式

python code

CNT = 0
N = 10

def dfs(cur):
if cur > N:
return

if cur == N:
global CNT
CNT += 1
return

dfs(cur+1)
dfs(cur+2)

dfs(0)

print(CNT)


累加返回式

python code

N = 10

def dfs(cur):
cnt = 0
if cur > N:
return 0

if cur == N:
return 1

cnt += dfs(cur+1)
cnt += dfs(cur+2)
return cnt

ans = dfs(0)
print(ans)


递归定义式

python code

def dfs(cur):
if cur==1 or cur==2:
return cur
return dfs(cur-1)+dfs(cur-2)

ans = dfs(10)
print(ans)


记忆化搜索 O(N)+?

用DP数组来保存已搜索过的当前节点搜索结果, 防止重复向下搜索.

累加返回式

python code

N = 10

C = [-1 for i in range(N+1)]

def dfs(cur):
if cur > N:
return 0

if cur == N:
return 1

if C[cur] != (-1):
return C[cur]

C[cur] = 0

C[cur] += dfs(cur+1)
C[cur] += dfs(cur+2)
return C[cur]

ans = dfs(0)
print(ans)


递归定义式

状态方程: dp[i]=dp[i-1]+dp[i-2]

python code

N = 10

C = [-1 for i in range(N+1)]
C[1], C[2] = 1, 2

def dp(cur):
if C[cur] != (-1):
return C[cur]

C[cur] = dp(cur-1) + dp(cur-2)
return C[cur]

ans = dp(10)
print(ans)


递推(动态规划) O(N)

状态方程: dp[i]=dp[i-1]+dp[i-2]

根据递归定义式的状态方程直接递推

F列表中保存的结果正好是一个斐波那契数列

N = 10

F = [0 for i in range(N+1)]
F[1], F[2] = 1, 2

for i in range(3, N+1):
F[i] = F[i-1] + F[i-2]

print(F
)


状态方程的转换

数塔

图如下

A

/ \

B C

/ \ / \

D E F

问题: 从顶层走到底层(不能回头), 每步只能走到相邻的结点, 求经过的结点的数字的最大和.

状态方程: T[i][j] = max(DP[i][j]+DP[i+1][j], DP[i][j]+DP[i+1][j+1])

N = 5
DP = [
[7],
[3, 8],
[8, 1, 0],
[2, 7, 4, 4],
[4, 5, 2, 6, 5]
]

for i in range(N-2, -1, -1):
for j in range(i+1):
DP[i][j] += max(DP[i+1][j], DP[i+1][j+1])

print(DP[0][0])


圆盘摆物

问题: 一个圆被分为3个区域A, B, C, 有一个物品在A区域, 一次可以把物品拿起来摆放在其他区域, 不能在原区域摆放, 这个步骤执行N次, 要求最后物品还是摆放在A区域, 有多少种方法?

状态方程如下

A[i] = B[i-1]+C[i-1]

B[i] = A[i-1]+C[i-1]

C[i] = A[i-1]+B[i-1]

化简后的状态方程如下

A[i] = 2A[i-2]+A[i-1]

python code

N = 10

A = [0 for i in range(N+1)]

A[1], A[2] = 0, 2

for i in range(3, N+1):
A[i] = 2*A[i-2] + A[i-1]

print(A
)


最大公共子序列

问题: 求2个字符串的最大公共子序列长度(元素不要求连续, 只含英文字母).

状态方程如下

L[i][j] = 0 if i==0 or j==0

L[i][j] = L[i-1][j-1] + 1 if str[i] == str[j]

L[i][j] = max(L[i-1][j], L[i][j-1]) if str[i] != str[j]

python code

S1 = "_ACBDEFGH"
S2 = "_ABCEGDH"

N, M = len(S1), len(S2)

DP = [[0 for j in range(M)] for i in range(N)]

for i in range(1, N):
for j in range(1, M):
if S1[i] == S2[j]:
DP[i][j] = DP[i-1][j-1] + 1
elif S1[i] != S2[j]:
DP[i][j] = max(DP[i-1][j], DP[i][j-1])

print(DP[N-1][M-1])


背包问题

0-1背包

问题: 有n个物品和1个背包, 物品i的重量是wi, 价值为vi, 背包的容量(重量)为C, 如何选择装入背包的物品使得装入背包中物品的总价值最大?

状态方程如下

DP[i][w] = max(DP[i-1][w], DP[i-1][w-W[i]]+V[i]) if w>=W[i]

DP[i][w] =DP[i-1][w] if w小于W[i]

python code

N, C = 5, 10

W = [0, 4, 1, 3, 4, 6]
V = [0, 4, 2, 7, 8, 6]

DP = [[0 for j in range(C+1)] for i in range(N+1)]

for i in range(1, N+1):
for j in range(1, C+1):
if W[i] <= j:
DP[i][j] = max(DP[i-1][j], DP[i-1][j-W[i]]+V[i])
else:
DP[i][j] = DP[i-1][j]

print(DP
[C])


完全背包

问题: 有n种物品(物品有无限个)和1个背包, 物品i的重量是wi, 价值为vi, 背包的容量(重量)为C, 如何选择装入背包的物品使得装入背包中物品的总价值最大?

状态方程: DP[i][w] = max(DP[i][w], DP[i-1][w], DP[i-1][w-W[i]*k]+V[i]*k)

python code

N, C = 5, 10

W = [0, 4, 2, 6, 5, 4]
V = [0, 4, 2, 5, 4, 6]

DP = [[0 for j in range(C+1)] for i in range(N+1)]

for i in range(1, N+1):
for j in range(1, C+1):
for k in range(j//W[i]+1):
DP[i][j] = max(DP[i][j], DP[i-1][j], DP[i-1][j-W[i]*k]+V[i]*k)

print(DP
[C])


多重背包

问题: 有n种物品(物品各有Ai(Ai>0)个)和1个背包, 物品i的重量是wi, 价值为vi, 背包的容量(重量)为C, 如何选择装入背包的物品使得装入背包中物品的总价值最大?

状态方程: DP[i][w] = max(DP[i][w], DP[i-1][w], DP[i-1][w-W[i]*k]+V[i]*k)

python code

N, C = 5, 10

W = [0, 4, 2, 6, 5, 4]
V = [0, 4, 3, 5, 4, 6]
A = [0, 1, 2, 3, 2, 1]

DP = [[0 for j in range(C+1)] for i in range(N+1)]

for i in range(1, N+1):
for j in range(1, C+1):
for k in range(min(j//W[i], A[i])+1):
DP[i][j] = max(DP[i][j], DP[i-1][j], DP[i-1][j-W[i]*k]+V[i]*k)

print(DP
[C])


背包问题优化

01背包的优化

在二维数组中计算的状态可以用一维数组覆盖计算, 并省去j小于W[i]的情况.

python code

N, C = 5, 10

W = [0, 4, 1, 3, 4, 6]
V = [0, 4, 2, 7, 8, 6]

F = [0 for i in range(C+1)]

for i in range(1, N+1):
for j in range(C, W[i]-1, -1):
F[j] = max(F[j], F[j-W[i]]+V[i])

print(F[C])


完全背包的优化

使用覆盖计算, 省去j小于W[i]的情况, 顺序计算第i个物品K个的价值.

python code

N, C = 5, 10

W = [0, 4, 1, 3, 4, 6]
V = [0, 4, 2, 7, 8, 6]

F = [0 for i in range(C+1)]

for i in range(1, N+1):
for j in range(W[i], C+1):
F[j] = max(F[j], F[j-W[i]]+V[i])

print(F[C])


多重背包的优化

将第i种物品分成n个独立物品的组合(转为01背包问题), 利用二进制分解优化, 再进行覆盖计算.

python code

N, C = 5, 10

W = [0, 4, 2, 6, 5, 4]
V = [0, 4, 3, 5, 4, 6]
A = [0, 1, 2, 3, 2, 1]

for i in range(N):
k, cnt = 2, A[i]-1
while k <= cnt:
W.append(k*W[i])
V.append(k*V[i])
cnt -= k
k <<= 1
N += 1

if cnt > 0:
W.append(cnt*W[i])
V.append(cnt*V[i])
N += 1

F = [0 for i in range(C+1)]

for i in range(1, N+1):
for j in range(C, W[i]-1, -1):
F[j] = max(F[j], F[j-W[i]]+V[i])

print(F[C])
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: