线性DP
Posted ShadowAA
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性DP相关的知识,希望对你有一定的参考价值。
线性DP
最长公共子序列
O(n*m)写法
int a[MAXN], b[MAXM], f[MAXN][MAXM];
int dp()
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (a[i] == b[j])
f[i][j] = f[i - 1][j - 1] + 1;
else
f[i][j] = std::max(f[i - 1][j], f[i][j - 1]);
return f[n][m];
O(nlogn)写法
可以转化为最长上升子序列(LIS)来求,时间复杂度可以优化到nlogn。
#include<bits/stdc++.h>
using namespace std;
int n,i,x,y,j,m,k,f[100005],c[100005],g[100005];
int main()
cin>>n;
for (i=1;i<=n;i++)
cin>>x;
c[x]=i;
for (i=1;i<=n;i++)
cin>>y;
f[i]=c[y];
memset(g,127,sizeof(g));
g[0]=0;
for (i=1;i<=n;i++)
k=lower_bound(g,g+m+1,f[i])-g-1;
if (k==m)
m++;
g[k+1]=min(g[k+1],f[i]);
cout<<m<<endl;
最长不下降子序列
O(n2)写法
设f[i]表示以i为结尾最长的长度。
int a[MAXN], d[MAXN];
int dp()
d[1] = 1;
int ans = 1;
for (int i = 2; i <= n; i++)
d[i] = 1;
for (int j = 1; j < i; j++)
if (a[j] <= a[i])
d[i] = max(d[i], d[j] + 1);
ans = max(ans, d[i]);
return ans;
O(nlogn)写法
memset(g,127,sizeof(g));
g[0]=0;
for (i=1;i<=n;i++)
k=lower_bound(g,g+m+1,f[i])-g-1;
if (k==m)
m++;
g[k+1]=min(g[k+1],f[i]);
cout<<m<<endl;
字符串间的编辑距离
洛谷2758
#include<bits/stdc++.h>
using namespace std;
string a, b; int lena, lenb, i, j, f[2005][2005];
int main()
cin >> a >> b;
lena = a.length();
lenb = b.length();
a = \'.\' + a; b = \'.\' + b;
for (i = 1; i <= lena; i++)
f[i][0] = i;
for (i = 1; i <= lenb; i++)
f[0][i] = i;
for (i = 1; i <= lena; i++)
for (j = 1; j <= lenb; j++)
if (a[i] == b[j])
f[i][j] = f[i - 1][j - 1];
else
f[i][j] = min(f[i - 1][j], f[i - 1][j - 1], f[i][j - 1]) + 1;
cout << f[lena][lenb] << \'\\n\';
例题
括号序列
第十二届蓝桥杯软件类C/C++大学B组省赛
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
using namespace std;
char s[5005]; long long n, u, v, i, f[5005][5005];
const int mod = 1e9 + 7;
int dp()
memset(f, 0, sizeof(f));
f[0][0] = 1;
for (int i = 1; i <= n; i++)
if (s[i] == \'(\')
for (int j = 1; j <= n; j++)
f[i][j] = f[i - 1][j - 1];
else
f[i][0] = (f[i - 1][0] + f[i - 1][1]) % mod;
for (int j = 1; j <= n; j++)
f[i][j] = (f[i - 1][j + 1] + f[i][j - 1]) % mod;
for (int i = 0; i <= n; i++)
if (f[n][i])
return f[n][i];
return -1;
int main()
scanf("%s", s + 1);
n = strlen(s + 1);
u = dp();
reverse(s + 1, s + n + 1);
for (i = 1; i <= n; i++)
if (s[i] == \'(\')
s[i] = \')\';
else s[i] = \'(\';
v = dp();
cout << (u * v) % mod << \'\\n\';
Research Rover
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m, k, i, j, f[2005][25], b[200005], c[200005], d[200005], s, p, l, q;
const int mod = 1e9 + 7;
struct ff
int x, y;
a[2005];
ll poww(ll x, ll y)
if (x == 0)
return 1;
ll k = poww(x / 2, y) % mod;
if (x % 2 == 1)
return (k * k % mod) * y % mod;
else return k * k % mod;
ll C(ll m, ll n)
return (((c[n] * d[n - m]) % mod) * b[m]) % mod;
bool cmp(ff t, ff w)
if ((t.x < w.x) or (t.x == w.x) and (t.y < w.y))
return 1;
return 0;
int main()
cin >> n >> m >> k >> p;
for (i = 1; i <= k; i++)
cin >> a[i].x >> a[i].y;
b[0] = 1; c[0] = 1; d[0] = 1;
for (i = 1; i <= 200000; i++)
b[i] = (poww(mod - 2, i) * b[i - 1]) % mod;
for (i = 1; i <= 200000; i++)
c[i] = c[i - 1] * i % mod;
d[i] = poww(mod - 2, c[i]) % mod;
sort(a, a + k + 1, cmp);
if ((a[k].x != n) or (a[k].y != m))
k++; a[k].x = n; a[k].y = m;
else p = p - p / 2;
if ((a[1].x == 1) and (a[1].y == 1))
for (i = 1; i < k; i++)
a[i].x = a[i + 1].x;
a[i].y = a[i + 1].y;
k--;
p = p - p / 2;
for (i = 1; i <= k; i++)
f[i][0] = C(a[i].x - 1, a[i].x - 1 + a[i].y - 1);
for (j = 1; j < i; j++)
if (a[j].y <= a[i].y)
for (l = 1; l <=20; l++)
f[i][l] = (f[i][l] + (f[j][l - 1] * C(a[i].x - a[j].x, a[i].x - a[j].x + a[i].y - a[j].y)) % mod) % mod;
f[i][l] = (f[i][l] - (f[j][l] * C(a[i].x - a[j].x, a[i].x - a[j].x + a[i].y - a[j].y)) % mod) % mod;
if (f[i][l] < 0)f[i][l] += mod;
for (l = 0; l <= 20; l++)
q = (q + f[k][l] * p % mod) % mod;
q = (q - f[k][l + 1] * p % mod) % mod;
if (q < 0) q += mod;
p = p - p / 2;
s = poww(mod - 2, f[k][0]) * q % mod;
cout << s << \'\\n\';
嘻嘻
线性dp
线性dp应该是dp中比较简单的一类,不过也有难的。(矩乘优化递推请出门右转)
传球游戏:https://www.luogu.org/problemnew/show/P1057
题意概述:一些人围成一个圈,每次可以把球传给左右两个人,求m步后回到第一个人手里的方案数。
这题大概也可以矩乘?不过递推就可以了,$dp[i][j]$表示传了j步,现在在i手里的方案数。转移:
$dp[i][j]=dp[i+1][j-1]+dp[i-1][j-1]$
边界再处理一下就行了。
# include <cstdio> # include <iostream> using namespace std; int n,m; long long dp[31][31]; int main() { scanf("%d%d",&n,&m); dp[1][0]=1; for (int i=1;i<=m;i++) { dp[1][i]=dp[2][i-1]+dp[n][i-1]; dp[n][i]=dp[n-1][i-1]+dp[1][i-1]; for (int j=2;j<n;j++) dp[j][i]=dp[j-1][i-1]+dp[j+1][i-1]; } printf("%d",dp[1][m]); return 0; }
以上是关于线性DP的主要内容,如果未能解决你的问题,请参考以下文章