线性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\';

例题

括号序列

括号序列 - 蓝桥云课 (lanqiao.cn)

第十二届蓝桥杯软件类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

Problem - 722E - Codeforces

#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的主要内容,如果未能解决你的问题,请参考以下文章

UVA-10271 Chopsticks (线性DP)

线性DP+树形DP

线性DP+树形DP

线性DP

线性dp

线性DP的几种模型和例题