Day13
元旦放了自己两天假舒舒服服跨个年,今天继续DP,提速提速
状态机模型
这道题可以用背包问题的思考方式根据第
i
i
i 家商铺选不选划分集合,状态表示也和背包大径相同:
f
[
i
]
f[i]
f[i] 表示前
i
i
i 家商铺最大价值。不选这家那就是
f
[
i
−
1
]
f[i - 1]
f[i−1] ,如果选这一家,那前一家必不选,那么就是
f
[
i
−
2
]
+
w
f[i - 2] + w
f[i−2]+w 很好理解
状
态
划
分
=
{
选
第
i
家
店
铺
f
[
i
−
2
]
+
w
不
选
第
i
家
商
铺
f
[
i
−
1
]
状态划分 = \begin{cases} 选第i家店铺 &&f[i - 2] + w\\ \\不选第i家商铺 && f[i - 1]\end{cases}
状态划分=⎩⎪⎨⎪⎧选第i家店铺不选第i家商铺f[i−2]+wf[i−1]
那状态转移方程就是:
f
[
i
]
=
m
a
x
(
f
[
i
−
1
]
,
f
[
i
−
2
]
+
w
)
f[i] = max(f[i - 1], f[i - 2] + w)
f[i]=max(f[i−1],f[i−2]+w)
#include<bits/stdc++.h>
using namespace std;
int main(void)
{
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> f(n + 2);
for (int i = 2; i < n + 2; i++) {
int w;
cin >> w;
f[i] = max(f[i - 2] + w, f[i - 1]);
}
cout << f[n + 1] << endl ;
}
return 0;
}
那么我们如何利用状态机的思考方式来解决这道题目,其实也很简单。
我们将一个店铺选和不选两种状态用
f
[
i
,
0
]
f[i, 0]
f[i,0] 和
f
[
i
,
1
]
f[i, 1]
f[i,1] 来表示,分开进行状态转移,最后取
m
a
x
max
max 就可以了,如果前一个店铺是不选的,那么当前商铺就是可选可不选两种情况,如果前一个店铺已经选了,那么当前店铺只能不选,所以状态转移方程就很好理解了。
状
态
转
移
=
{
f
[
i
,
0
]
=
m
a
x
(
f
[
i
−
1
,
0
]
,
f
[
i
−
1
,
1
]
)
f
[
i
,
1
]
=
f
[
i
−
1
,
0
]
+
w
状态转移 = \begin{cases} f[i, 0] = max(f[i - 1, 0], f[i - 1, 1]) \\ \\ f[i, 1] = f[i - 1, 0] + w \end{cases}
状态转移=⎩⎪⎨⎪⎧f[i,0]=max(f[i−1,0],f[i−1,1])f[i,1]=f[i−1,0]+w
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int f[N][2];
int t, n, w;
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> t;
while (t--) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> w;
f[i][0] = max(f[i - 1][0], f[i - 1][1]);
f[i][1] = f[i - 1][0] + w;
}
cout << max(f[n][0], f[n][1]) << endl ;
}
return 0;
}
状态表示:
f
[
i
,
j
,
0
]
和
f
[
i
,
j
,
1
]
f[i, j, 0] 和 f[i, j, 1]
f[i,j,0]和f[i,j,1] 表示前
i
i
i 天经过
j
j
j 次交易且手上有无买入股票的利润
状
态
转
移
=
{
第
i
天
经
过
j
次
交
易
无
买
入
f
[
i
,
j
,
0
]
=
m
a
x
(
f
[
i
−
1
,
j
,
0
]
,
f
[
i
−
1
,
j
,
1
]
+
w
)
第
i
天
经
过
j
次
交
易
有
买
入
f
[
i
,
j
,
1
]
=
m
a
x
(
f
[
i
−
1
,
j
,
1
]
,
f
[
i
−
1
,
j
−
1
,
0
]
−
w
)
状态转移 = \begin{cases}第i天经过j次交易无买入& f[i, j, 0] = max(f[i - 1, j, 0], f[i - 1, j, 1] + w) \\ \\第i天经过j次交易有买入& f[i, j, 1] = max(f[i - 1, j, 1], f[i - 1, j - 1, 0] - w)\end{cases}
状态转移=⎩⎪⎨⎪⎧第i天经过j次交易无买入第i天经过j次交易有买入f[i,j,0]=max(f[i−1,j,0],f[i−1,j,1]+w)f[i,j,1]=max(f[i−1,j,1],f[i−1,j−1,0]−w)
#include<bits/stdc++.h>
using namespace std;
const int N = 100010, M = 110, INF = 0x3f3f3f3f;
int n, m, w;
int f[N][M][2];
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
memset(f, -0x3f, sizeof f); // 全部初始化为负无穷
for (int i = 0; i <= n; i++) f[i][0][0] = 0; // 0次交易必然没有买入且利润为0,其他状态都非法
for (int i = 1; i <= n; i++) {
cin >> w;
for (int j = 1; j <= m; j++) {
f[i][j][0] = max(f[i - 1][j][0], f[i - 1][j][1] + w); // 状态转移
f[i][j][1] = max(f[i - 1][j][1], f[i - 1][j - 1][0] - w);
}
}
int res = 0;
for (int i = 0; i <= m; i++) res = max(res, f[n][i][0]);
cout << res << endl ;
return 0;
}