動規以空間換取時間

動態規劃刷題總結以下型別及leetcode題目:

揹包

416 分割等和子集(medium)

494 目標和(medium)

474 一和零(medium)

322 零錢兌換(medium)

518 零錢兌換 II(medium)

139 單詞拆分(medium)

377 組合總和 Ⅳ(medium)

1維寫法簡單總結

1。如果是0-1揹包,即陣列中的元素不可重複使用,nums放在外迴圈,target在內迴圈,且內迴圈倒序;

for num in nums:

for i in range(target, num-1, -1):

2。如果是完全揹包,即陣列中的元素可重複使用,nums放在外迴圈,target在內迴圈。且內迴圈正序。

for num in nums:

for i in range(num, target+1):

3。如果組合問題需考慮元素之間的順序,需將target放在外迴圈,將nums放在內迴圈。

for i in range(1, target+1):

for num in nums:

揹包問題

416 分割等和子集(medium)

https://

leetcode-cn。com/problem

s/partition-equal-subset-sum/

動規(空間未最佳化)

class

Solution

def

canPartition

self

nums

List

int

])

->

bool

# 將揹包容量看作sum的一半

if

not

nums

return

False

sum_

=

sum

nums

if

sum_

%

2

!=

0

return

False

target

=

sum_

//

2

# 有n個物品,容量為target,每個物品的價值與物品的重量一樣

dp

=

[[

-

1

for

_

in

range

target

+

1

)]

for

_

in

range

len

nums

))]

# 先初始化第一行(將第零個物品裝入內部)

for

i

in

range

target

+

1

):

dp

0

][

i

=

nums

0

if

nums

0

<=

i

else

0

#處理dp表的後邊幾行

for

i

in

range

1

len

nums

)):

for

j

in

range

target

+

1

):

# 不放置第i個物品

dp

i

][

j

=

dp

i

-

1

][

j

if

nums

i

<=

j

dp

i

][

j

=

max

dp

i

][

j

],

nums

i

+

dp

i

-

1

][

j

-

nums

i

]])

# print(dp)

return

dp

-

1

][

-

1

==

target

空間最佳化動規

class

Solution

def

canPartition

self

nums

List

int

])

->

bool

# 將揹包容量看作sum的一半

if

not

nums

return

False

sum_

=

sum

nums

if

sum_

%

2

!=

0

return

False

target

=

sum_

//

2

# 有n個物品,容量為target,每個物品的價值與物品的重量一樣

# 實際上狀態僅僅與上一行的狀態有關,因此只需要維持兩行的空間即可

dp

=

[[

-

1

for

_

in

range

target

+

1

)]

for

_

in

range

2

)]

# 先初始化第一行(將第零個物品裝入內部)

for

i

in

range

target

+

1

):

dp

0

][

i

=

nums

0

if

nums

0

<=

i

else

0

#處理dp表的後邊幾行,僅僅維持兩行,可以將偶數行放置在dp表的第0行,奇數行放置在dp表的第1行

for

i

in

range

1

len

nums

)):

for

j

in

range

target

+

1

):

# 不放置第i個物品

dp

i

%

2

][

j

=

dp

[(

i

-

1

%

2

][

j

if

nums

i

<=

j

dp

i

%

2

][

j

=

max

dp

i

%

2

][

j

],

nums

i

+

dp

[(

i

-

1

%

2

][

j

-

nums

i

]])

# print(dp)

return

dp

[(

len

nums

-

1

%

2

][

-

1

==

target

class

Solution

def

canPartition

self

nums

List

int

])

->

bool

‘’‘

01揹包

1維寫法

’‘’

if

not

nums

return

True

sum_

=

sum

nums

if

sum_

&

1

return

False

target

=

sum_

//

2

dp

=

0

for

_

in

range

target

+

1

)]

for

i

in

range

target

+

1

):

if

nums

0

<=

i

dp

i

=

nums

0

for

num

in

nums

1

:]:

## 01揹包問題倒序,這裡不採用遍歷到0,因為揹包容量小於num,裝不進去,提前終止

for

i

in

range

target

num

-

1

-

1

):

dp

i

=

max

dp

i

],

dp

i

-

num

+

num

# print(dp)

return

dp

-

1

==

target

494 目標和(medium)

https://

leetcode-cn。com/problem

s/target-sum/

class

Solution

def

findTargetSumWays

self

nums

List

int

],

S

int

->

int

‘’‘

01揹包問題:target為計算後的target,找子序列和為target的個數

揹包容量為target,物品重量為元素的值

dp[i][j] i為考慮前i個元素,j為容量,dp[i][j]表示目標和為j的方法數

狀態轉移方程為:dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]]

’‘’

x

=

sum

nums

if

x

<

S

return

0

tm

=

x

+

S

if

tm

&

1

return

0

target

=

int

((

tm

/

2

dp

=

[[

0

for

_

in

range

target

+

1

)]

for

_

in

range

len

nums

+

1

)]

dp

0

][

0

=

1

for

i

in

range

1

len

nums

+

1

):

for

j

in

range

target

+

1

):

if

j

>=

nums

i

-

1

]:

dp

i

][

j

=

dp

i

-

1

][

j

+

dp

i

-

1

][

j

-

nums

i

-

1

]]

else

dp

i

][

j

=

dp

i

-

1

][

j

# print(dp)

return

dp

-

1

][

-

1

class

Solution

def

findTargetSumWays

self

nums

List

int

],

S

int

->

int

‘’‘

01揹包問題 1維寫法

sum(P) - sum(N) = target

sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N)

2 * sum(P) = target + sum(nums)

target為計算後的target,找子序列和為target的個數

’‘’

x

=

sum

nums

if

x

<

S

return

0

tm

=

x

+

S

if

tm

&

1

return

0

target

=

int

((

tm

/

2

dp

=

0

for

_

in

range

target

+

1

)]

dp

0

=

1

for

num

in

nums

for

j

in

range

target

num

-

1

-

1

):

dp

j

+=

dp

j

-

num

# print(dp)

return

dp

-

1

474 一和零(medium)

https://

leetcode-cn。com/problem

s/ones-and-zeroes/

思路:等同於416題的思路

class

Solution

def

findMaxForm

self

strs

List

str

],

m

int

n

int

->

int

‘’‘

轉換為0 1揹包問題

dp[i][j][k], j為0的容量, k為1的容量,i為裝入字串的個數,字串相當於物品,價值為1

分兩種情況,第一將第i個字串加入揹包,此時,dp[i][j][k] = dp[i-1][j-0的使用個數][k-1的使用個數]+1

第二種,不將第i個字串加入揹包,此使dp[i][j][k] = dp[i-1][j][k]

’‘’

dp

=

[[[

0

for

_

in

range

n

+

1

)]

for

_

in

range

m

+

1

)]

for

_

in

range

len

strs

))]

one

=

strs

0

count

‘1’

zero

=

strs

0

count

‘0’

for

i

in

range

m

+

1

):

for

j

in

range

n

+

1

):

if

i

>=

zero

and

j

>=

one

dp

0

][

i

][

j

=

1

for

i

in

range

1

len

strs

)):

one

=

strs

i

count

‘1’

zero

=

strs

i

count

‘0’

for

j

in

range

m

+

1

):

for

k

in

range

n

+

1

):

dp

i

][

j

][

k

=

dp

i

-

1

][

j

][

k

if

j

>=

zero

and

k

>=

one

dp

i

][

j

][

k

=

max

dp

i

][

j

][

k

],

dp

i

-

1

][

j

-

zero

][

k

-

one

+

1

# print(dp)

return

dp

-

1

][

-

1

][

-

1

class

Solution

def

findMaxForm

self

strs

List

str

],

m

int

n

int

->

int

‘’‘

轉換為0 1揹包問題 2維寫法(類似於1維寫法)

’‘’

dp

=

[[

0

for

_

in

range

n

+

1

)]

for

_

in

range

m

+

1

)]

one

=

strs

0

count

‘1’

zero

=

strs

0

count

‘0’

for

i

in

range

m

+

1

):

for

j

in

range

n

+

1

):

if

i

>=

zero

and

j

>=

one

dp

i

][

j

=

1

for

i

in

range

1

len

strs

)):

one

=

strs

i

count

‘1’

zero

=

strs

i

count

‘0’

for

j

in

range

m

zero

-

1

-

1

):

for

k

in

range

n

one

-

1

-

1

):

dp

j

][

k

=

max

dp

j

][

k

],

dp

j

-

zero

][

k

-

one

+

1

# print(dp)

return

dp

-

1

][

-

1

322 零錢兌換(medium)

https://

leetcode-cn。com/problem

s/coin-change/

class

Solution

def

coinChange

self

coins

List

int

],

amount

int

->

int

‘’‘

完全揹包問題,揹包的容量為amount,物品的重量為coins的面額

硬幣的個數無限。

剛好裝滿揹包硬幣數目最少。

dp[i][j]表示前i種硬幣,組合成j的最小的硬幣個數

狀態轉移:

對於第i種硬幣,選擇不拿,或者拿1到k個,其中最小的值

dp[i][j] = min(dp[i-1][j], dp[i-1][j-c]+1, dp[i-1][j-2*c]+2, 。。。,dp[i-1][j-c*k]+k)

同時,對於dp[i][j-c]= min(dp[i-1][j-c], dp[i-1][j-c*2]+1, dp[i-1][j-c*k]+k-1)

因此去掉冗餘的計算

dp[i][j] = min(dp[i-1][j], dp[i][j-c]+1)

’‘’

dp

=

[[

float

‘inf’

for

_

in

range

amount

+

1

)]

for

_

in

range

len

coins

+

1

)]

dp

0

][

0

=

0

for

i

in

range

1

len

coins

+

1

):

for

j

in

range

amount

+

1

):

dp

i

][

j

=

dp

i

-

1

][

j

if

coins

i

-

1

<=

j

dp

i

][

j

=

min

dp

i

][

j

],

dp

i

][

j

-

coins

i

-

1

]]

+

1

# print(dp)

return

dp

-

1

][

-

1

if

dp

-

1

][

-

1

!=

float

‘inf’

else

-

1

class

Solution

def

coinChange

self

coins

List

int

],

amount

int

->

int

‘’‘

完全揹包問題,揹包的容量為amount,物品的重量為coins的面額

硬幣的個數無限。 1維寫法

’‘’

dp

=

float

‘inf’

for

_

in

range

amount

+

1

)]

dp

0

=

0

for

i

in

range

len

coins

)):

for

j

in

range

coins

i

],

amount

+

1

):

dp

j

=

min

dp

j

],

dp

j

-

coins

i

]]

+

1

# print(dp)

return

dp

-

1

if

dp

-

1

!=

float

‘inf’

else

-

1

518 零錢兌換 II(medium)

https://

leetcode-cn。com/problem

s/coin-change-2/

class

Solution

def

change

self

amount

int

coins

List

int

])

->

int

‘’‘

完全揹包問題

dp[i][j]表示考慮前i種硬幣,湊成金額為j的組合數

狀態轉移:

dp[i][j] = dp[i-1][j]+dp[i-1][j-c]+dp[i-1][j-2*c]+。。。+dp[i-1][j-k*c]

由於dp[i][j-c] = dp[i-1][j-c] + dp[i-1][j-2*c] + 。。。+dp[i-1][j-k*c]

因此dp[i][j] = dp[i-1][j] + dp[i][j-c]

’‘’

if

not

coins

if

amount

return

0

else

return

1

dp

=

[[

0

for

_

in

range

amount

+

1

)]

for

_

in

range

len

coins

))]

for

i

in

range

amount

+

1

):

if

i

%

coins

0

==

0

dp

0

][

i

=

1

for

i

in

range

1

len

coins

)):

for

j

in

range

amount

+

1

):

if

j

<

coins

i

]:

dp

i

][

j

=

dp

i

-

1

][

j

else

dp

i

][

j

=

dp

i

-

1

][

j

+

dp

i

][

j

-

coins

i

]]

# print(dp)

return

dp

-

1

][

-

1

class

Solution

def

change

self

amount

int

coins

List

int

])

->

int

‘’‘

完全揹包問題 1維寫法

’‘’

if

not

coins

if

amount

return

0

else

return

1

dp

=

0

for

_

in

range

amount

+

1

)]

dp

0

=

1

for

i

in

range

len

coins

)):

for

j

in

range

coins

i

],

amount

+

1

):

dp

j

+=

dp

j

-

coins

i

]]

# print(dp)

return

dp

-

1

139 單詞拆分(medium)

https://

leetcode-cn。com/problem

s/word-break/

class

Solution

def

wordBreak

self

s

str

wordDict

List

str

])

->

bool

dp

=

False

*

len

s

+

1

dp

0

=

True

for

i

in

range

1

len

s

+

1

):

for

word

in

wordDict

if

i

>=

len

word

and

s

i

-

len

word

):

i

==

word

):

dp

i

=

dp

i

or

dp

i

-

len

word

)]

return

dp

-

1

377 組合總和 Ⅳ(medium)

https://

leetcode-cn。com/problem

s/combination-sum-iv/

遞迴方法,超時

class

Solution

def

combinationSum4

self

nums

List

int

],

target

int

->

int

‘’‘

DFS,先用遞迴

’‘’

def

helper

nums

target

):

if

target

<

0

return

if

target

==

0

self

res

+=

1

for

i

in

range

len

nums

)):

helper

nums

target

-

nums

i

])

self

res

=

0

if

not

nums

return

0

helper

nums

target

return

self

res

class

Solution

def

combinationSum4

self

nums

List

int

],

target

int

->

int

‘’‘

與完全揹包問題有所不同,這裡實際上是排列問題,不同順序的組合看作是不同的結果

dp[i]表示組成目標為i的組合數的數目

’‘’

if

not

nums

if

target

return

0

else

return

1

dp

=

0

*

target

+

1

dp

0

=

1

for

i

in

range

1

target

+

1

):

for

num

in

nums

if

i

>=

num

dp

i

+=

dp

i

-

num

return

dp

-

1