dp引入
[IOI1994]数字三角形 Number Triangles
问题描述:略。
转移方程:
F
(
i
,
j
)
=
A
[
i
,
j
]
+
m
a
x
{
F
(
i
−
1
,
j
)
F
(
i
−
1
,
j
−
1
)
i
f
j
>
1
F(i, j) = A[i, j] + max \begin{cases} F(i - 1, j) \\ F(i - 1, j - 1)\quad if\quad j > 1 \end{cases}
F(i,j)=A[i,j]+max{F(i−1,j)F(i−1,j−1)ifj>1
状态表示:
F(i, j)
表示从左上角走到第i
行第j
列的和得最大值。
边界:
F
(
i
,
j
)
=
A
[
i
,
j
]
F(i, j) = A[i, j]
F(i,j)=A[i,j]
目标:
m
a
x
1
≤
j
≤
N
F
(
N
,
j
)
max_{1\leq j\leq N}{F(N, j)}
max1≤j≤NF(N,j)
代码:
void solve() {
int n; cin>>n;
vector<vector<int>> ph(n+1, vector<int>(n+1));
auto f(ph);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= i; ++j) {
cin>>ph[i][j];
}
}
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= i; ++j) {
f[i][j] = max(f[i-1][j], f[i-1][j-1]) + ph[i][j];
}
}
cout<<*max_element(all(f[n]));
}
最长上升子序列(LIS)
问题描述:略。
转移方程:
F
(
i
)
=
m
a
x
0
≤
j
<
i
,
A
[
j
]
<
A
[
i
]
F
[
j
]
+
1
F(i) = max_{0 \leq j < i,A[j]<A[i]}{F[j]+1}
F(i)=max0≤j<i,A[j]<A[i]F[j]+1
状态表示:
F(i)
表示前i个最上上升子序列个数
边界:
F
(
0
)
=
0
F(0) = 0
F(0)=0
目标:
m
a
x
1
≤
i
≤
N
F
[
i
]
max_{1 \leq i \leq N}{F[i]}
max1≤i≤NF[i]
代码:
void solve() {
int n; cin>>n;
vector<int> a(n+1);
for(int i = 1; i <= n; ++i) cin>>a[i];
vector<int> f(n+1);
for(int i = 1; i <= n; ++i) {
f[i] = 1;
for(int j = 1; j < i; ++j) {
if(a[j] <= a[i]) f[i] = max(f[i], f[j] + 1);
}
}
cout<<*max_element(all(f));
}
void solve() {
int n; cin>>n;
vector<int> a(n);
for(auto &t: a) cin>>t;
vector<int> f;
for(auto t: a) {
auto pos = lower_bound(all(f), t);
if(pos == f.end()) f.push_back(t);
else *pos = t;
}
cout<<f.size();
}
最长公共子序列(LCS)
问题描述:略。
转移方程:
F
(
i
,
j
)
=
m
a
x
{
F
(
i
−
1
,
j
)
F
(
i
,
j
−
1
)
F
(
i
−
1
,
j
−
1
)
i
f
A
[
i
]
=
B
[
i
]
F(i, j) = max \begin{cases} F(i - 1, j) \\ F(i, j - 1) \\ F(i - 1, j - 1) \quad if \quad A[i] = B[i] \end{cases}
F(i,j)=max⎩
⎨
⎧F(i−1,j)F(i,j−1)F(i−1,j−1)ifA[i]=B[i]
状态表示:
在A串的第i个和B串的第j个中的最长公共子序列
边界:
F
(
i
,
0
)
=
F
(
0
,
j
)
=
0
F(i, 0) = F(0, j) = 0
F(i,0)=F(0,j)=0
目标:
F
(
N
i
,
M
j
)
F(N_{i}, M_{j})
F(Ni,Mj)
代码:
// 字符串和数字对于这题来说相同
void solve() {
int n; cin>>n;
vector<int> A(n+1);
auto B(A);
vector<vector<int>> f(n+1, vector<int>(n+1));
rep(i,1,n) cin>>A[i];
rep(i,1,n) cin>>B[i];
rep(i,1,n) {
rep(j,1,n) {
f[i][j] = max(f[i-1][j], f[i][j-1]);
if(A[i] == B[j]) f[i][j] = max(f[i][j], f[i-1][j-1] + 1);
}
}
cout<<f[n][n];
}
过河卒
问题描述:略。
转移方程:
F
(
i
,
j
)
=
{
0
i
f
p
h
[
i
,
j
]
=
马能到达位置
F
(
i
−
1
,
j
)
+
F
(
i
,
j
−
1
)
F(i, j) = \begin{cases} 0 \quad if \quad ph[i,j] = 马能到达位置 \\ F(i-1,j) + F(i,j-1) \end{cases}
F(i,j)={0ifph[i,j]=马能到达位置F(i−1,j)+F(i,j−1)
状态表示:
F(i,j)
表示从0,0点到坐标(i,j)的路径之和。
边界:
F
(
0
,
0
)
=
1
F(0,0) = 1
F(0,0)=1
目标:
F
(
N
,
N
)
F(N,N)
F(N,N)
代码:
void solve() {
int x1,x2,y1,y2; cin>>x1>>y1>>x2>>y2;
x1++,y1++,x2++,y2++;
vector<vector<int>> f(x1+1, vector<int>(y1+1));
vector<int> fx({-1,-1,1,1,2,2,-2,-2}), fy({2,-2,2,-2,1,-1,1,-1});
for(int i = 0; i < 8; ++i) {
int xx = x2 + fx[i], yy = y2 + fy[i];
if(xx < 1 || xx > x1 || yy < 1 || yy > y1) continue;
f[xx][yy] = -1;
}
f[x2][y2] = -1;
// f[0][2] = f[2][0] = 1;
for(int i = 1; i <= x1; ++i) {
for(int j = 1; j <= y1; ++j) {
if(i == 1 && j == 1) {
f[i][j] = 1; continue;
}
if(f[i][j] == -1) f[i][j] = 0;
else f[i][j] = f[i-1][j] + f[i][j-1];
// cout<<f[i][j]<<" ";
}
}
cout<<f[x1][y1];
}
luogu数据:
in: 8 6 0 4
out: 1617
错误原因:判断方向continue错了,xx > x2 || yy > y2
,原来是xx >= x2 || yy >= y2
。
练习
P1057 [NOIP2008 普及组] 传球游戏
问题描述:传球游戏,开始球在第一个人处,问经过m次传球,传到第一个人处的方法数。
转移方程:
F
(
j
=
=
1
?
n
:
j
−
1
,
i
)
+
=
F
(
j
,
i
−
1
)
F
(
j
=
=
n
?
1
:
j
+
1
,
i
)
+
=
F
(
j
,
i
−
1
)
F(j == 1 ? n : j -1 ,i) += F(j, i - 1)\\ F(j == n ? 1 : j + 1, i) += F(j, i - 1)
F(j==1?n:j−1,i)+=F(j,i−1)F(j==n?1:j+1,i)+=F(j,i−1)
状态表示:
F(j,i)
表示从0到第i次传到第j个人的方法数。因为是成环,需要在左右边界进行特判。
边界:
F
(
1
,
0
)
=
1
F(1,0) = 1
F(1,0)=1
目标:
F
(
1
,
m
)
F(1,m)
F(1,m)
代码:
void solve() {
int n,m; cin>>n>>m;
vector<vector<int>> f(n+1, vector<int>(m+1));
f[1][0] = 1;
rep(i,1,m) { // **
rep(j,1,n) {
f[j == 1 ? n : j - 1][i] += f[j][i-1];
f[j == n ? 1 : j + 1][i] += f[j][i-1];
}
}
cout<<f[1][m];
}
错误原因
- 没有理清子问题到底是什么。开始将子问题理解成,第j个人经i次传到下一次。但是真正的子问题是:第i次传球传给的人。 将n和m遍历顺序进行修改就正确了
最大子段和
问题描述:给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。
转移方程:
F
(
i
)
=
m
a
x
(
A
[
i
]
,
F
(
i
−
1
)
+
A
[
i
]
)
F(i) = max(A[i], F(i-1) + A[i])
F(i)=max(A[i],F(i−1)+A[i])
状态表示:
F(i)
表示的是前i个连续的最大字段和。
边界:
F
(
1
)
=
A
[
1
]
F(1) = A[1]
F(1)=A[1]
目标:
m
a
x
1
≤
i
≤
N
F
(
i
)
max_{1 \leq i \leq N}{F(i)}
max1≤i≤NF(i)
代码:
void solve() {
int n; cin>>n;
vector<LL> a(n+1);
for(int i = 1; i <= n; ++i) cin>>a[i];
vector<LL> f(n+1);
f[1] = a[1];
for(int i = 2; i <= n; ++i) {
f[i] = max(a[i], f[i-1] + a[i]);
}
cout<<*max_element(f.begin()+1, f.end());
}