动态规划是编程题中高频出现,且具有一定难度的算法。对于动态规划的解读参见本篇博客。下面我们以leetcode中的股票问题为例,使用动态规划算法解决问题。
leetcode121
根据题意可以知道,我们只能买卖一次股票,为了使得股票利润最大,我们可以推导得出如下方程。设
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]为第
i
i
i天买入股票后剩余的钱,
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]为卖出股票后剩余的钱。我们的目的则是,让买入股票后剩余的钱尽可能多,卖出股票后获取钱尽可能多。所以可以得到状态转移方程:
d
p
[
i
+
1
]
[
1
]
=
m
a
x
(
d
p
[
i
]
[
1
]
,
d
p
[
i
]
[
0
]
+
p
r
i
c
e
s
[
i
+
1
]
)
dp[i+1][1]=max(dp[i][1],dp[i][0]+prices[i+1])
dp[i+1][1]=max(dp[i][1],dp[i][0]+prices[i+1])
d
p
[
i
+
1
]
[
0
]
=
m
a
x
(
d
p
[
i
]
[
0
]
,
−
p
r
i
c
e
s
[
i
+
1
]
)
dp[i+1][0]=max(dp[i][0],-prices[i+1])
dp[i+1][0]=max(dp[i][0],−prices[i+1])
初始时,
d
p
[
0
]
[
0
]
=
−
p
r
i
c
e
s
[
0
]
,
d
p
[
0
]
[
1
]
=
0
dp[0][0]=-prices[0],dp[0][1]=0
dp[0][0]=−prices[0],dp[0][1]=0。
所以,代码如下:
class Solution {
public int maxProfit(int[] prices) {
if(prices.length<=1)
return 0;
int[][] dp=new int[prices.length][2];
dp[0][1]=0;dp[0][0]=-prices[0];
for(int i=0;i<prices.length-1;i++){
dp[i+1][1]=Math.max(dp[i][1],dp[i][0]+prices[i+1]);
dp[i+1][0]=Math.max(dp[i][0],-prices[i+1]);
}
return dp[prices.length-1][1];
}
}
但是我们实际上只需要保留上一天的买入股票剩余的钱与卖出股票获得的钱即可,对代码进行优化,令 c a s h = d p [ i ] [ 1 ] , h o l d = d p [ i ] [ 0 ] cash=dp[i][1],hold=dp[i][0] cash=dp[i][1],hold=dp[i][0],
class Solution {
public int maxProfit(int[] prices) {
if(prices.length<=1)
return 0;
int cash=0,hold=-prices[0];
for(int i=0;i<prices.length;i++){
cash=Math.max(cash,hold+prices[i]);
hold=Math.max(hold,-prices[i]);
}
return cash;
}
}
leetcode 122
根据题意我们可以知道,现在股票可以多次买入卖出。根据这个新特点,我们重新设计状态转移方程:
d
p
[
i
+
1
]
[
1
]
=
m
a
x
(
d
p
[
i
]
[
1
]
,
d
p
[
i
]
[
0
]
+
p
r
i
c
e
s
[
i
+
1
]
)
dp[i+1][1]=max(dp[i][1],dp[i][0]+prices[i+1])
dp[i+1][1]=max(dp[i][1],dp[i][0]+prices[i+1])
d
p
[
i
+
1
]
[
0
]
=
m
a
x
(
d
p
[
i
]
[
0
]
,
d
p
[
i
]
[
1
]
−
p
r
i
c
e
s
[
i
+
1
]
)
dp[i+1][0]=max(dp[i][0],dp[i][1]-prices[i+1])
dp[i+1][0]=max(dp[i][0],dp[i][1]−prices[i+1])
初始状态与上面相同,令
c
a
s
h
=
d
p
[
i
]
[
1
]
,
h
o
l
d
=
d
p
[
i
]
[
0
]
cash=dp[i][1],hold=dp[i][0]
cash=dp[i][1],hold=dp[i][0],对代码进行优化后得到:
class Solution {
public int maxProfit(int[] prices) {
if(prices.length<=1)
return 0;
int cash=0,hold=-prices[0];
for(int i=0;i<prices.length;i++){
cash=Math.max(cash,hold+prices[i]);
hold=Math.max(hold,cash-prices[i]);
}
return cash;
}
}
leetcode 714
此题与122题基本一样,我们可以认定,股票不会再同一天买入然后卖出,因为这样必然会亏本(扣除手续费fee)。所以状态转移方程可以确定为:
d
p
[
i
+
1
]
[
1
]
=
m
a
x
(
d
p
[
i
]
[
1
]
,
d
p
[
i
]
[
0
]
+
p
r
i
c
e
s
[
i
+
1
]
−
f
e
e
)
dp[i+1][1]=max(dp[i][1],dp[i][0]+prices[i+1]-fee)
dp[i+1][1]=max(dp[i][1],dp[i][0]+prices[i+1]−fee)
d
p
[
i
+
1
]
[
0
]
=
m
a
x
(
d
p
[
i
]
[
0
]
,
d
p
[
i
]
[
1
]
−
p
r
i
c
e
s
[
i
+
1
]
)
dp[i+1][0]=max(dp[i][0],dp[i][1]-prices[i+1])
dp[i+1][0]=max(dp[i][0],dp[i][1]−prices[i+1])
同样,将代码优化,
c
a
s
h
=
d
p
[
i
]
[
1
]
,
h
o
l
d
=
d
p
[
i
]
[
0
]
cash=dp[i][1],hold=dp[i][0]
cash=dp[i][1],hold=dp[i][0],得到
class Solution {
public int maxProfit(int[] prices, int fee) {
int cash=0,hold=-prices[0];
for(int i=0;i<prices.length;i++){
cash=Math.max(cash,hold+prices[i]-fee);
hold=Math.max(hold,cash-prices[i]);
}
return cash;
}
}