线性DP
AcWing 898. 数字三角形
给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输入格式
第一行包含整数 n,表示数字三角形的层数。
接下来 n行,每行包含若干整数,其中第 i 行表示数字三角形第 i层包含的整数。
输出格式
输出一个整数,表示最大的路径数字和。
数据范围
1≤n≤500
−10000≤三角形中的整数≤10000
输入样例:
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出样例:
30
思路:对于任意节点,我们都可以从左上方或者右上角的状态转移过来,于是可以用f[i,j]表示一个状态集合,译为从(1,1)到(i,j)的路经集合的最大值,状态转移为
f[i][j]=max(f[i-1][j-1],f[i-1][j])+a[i][j]分别对应左上角和右上角的状态,考虑边界情况,应该将边界位置初始化为负无穷.(至少的比给定数据最小值大很多……)
不理解的小伙伴看图
#include<iostream>
#include<algorithm>
using namespace std;
const int N=510,INF=1e9;
int a[N][N],f[N][N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
for(int j=0;j<=i+1;j++) f[i][j]=-INF;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++) cin>>a[i][j];
f[1][1]=a[1][1];
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
f[i][j]=max(f[i-1][j],f[i-1][j-1])+a[i][j];
int res=-INF;
for(int i=1;i<=n;i++)
res=max(res,f[n][i]);
cout<<res;
return 0;
}
AcWing 895. 最长上升子序列
给定一个长度为 N
的数列,求数值严格单调递增的子序列的长度最长是多少。
输入格式
第一行包含整数 N。
第二行包含 N个整数,表示完整序列。
输出格式
输出一个整数,表示最大长度。
数据范围
1≤N≤1000
−109≤数列中的数≤109
输入样例:
7
3 1 2 1 8 5 6
输出样例:
4
思路:我们可以用f[i]表示从1到i的所有上升子序列长度(明显是一个集合)的最大值(以a[i]结尾的最长上升子序列),状态更新应该枚举前i-1个数的f[i]集合(意思:从前面的最大上升子序列中取其加1,因为前面的长度已经最长,所以此时更新的状态也一定是最长的).
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int a[N],f[N];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
int res=-2e9;
for(int i=0;i<n;i++)
{
f[i]=1;
for(int j=0;j<i;j++)
if(a[i]>a[j]) f[i]=max(f[i],f[j]+1);
res=max(res,f[i]);
}
cout<<res;
return 0;
}
AcWing 896. 最长上升子序列2
给定一个长度为 N
的数列,求数值严格单调递增的子序列的长度最长是多少。
输入格式
第一行包含整数 N。
第二行包含 N 个整数,表示完整序列。
输出格式
输出一个整数,表示最大长度。
数据范围1≤N≤100000,
−109≤数列中的数≤109
输入样例:
7
3 1 2 1 8 5 6
输出样例:
4
思路:我们可以发现最长上升子序列每次更新时都是选大于原先某个长度的上升子序列最大值,如果此时进来的值不大于此时的值,我们选取第一个大于该数的f[k]将其替换成此时进来的值,采用贪心的策略来将时间复杂度优化到O(n*longn),
图示:(本人理解为什么可以进行贪心)
#include
#include
using namespace std;
const int N=100010;
int a[N],f[N],cnt;
int find(int x)
{
int l=1,r=cnt;
while(l<r)
{
int mid=l+r>>1;
if(f[mid]>=x) r=mid;
else l=mid+1;
}
return l;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
f[++cnt]=a[0];
for(int i=1;i<n;i++)
if(a[i]>f[cnt]) f[++cnt]=a[i];
else f[find(a[i])]=a[i];
cout<<cnt;
return 0;
}
AcWing 897. 最长公共子序列
给定两个长度分别为 N和 M的字符串 A和B,求既是 A 的子序列又是 B的子序列的字符串长度最长是多少。
输入格式第一行包含两个整数 N 和 M。
第二行包含一个长度为 N的字符串,表示字符串 A。
第三行包含一个长度为 M 的字符串,表示字符串 B。
字符串均由小写字母构成。
输出格式
输出一个整数,表示最大长度。
数据范围 1≤N,M≤1000
输入样例:
4 5
acbd
abedc
输出样例:
3
思路:用f[i,j]表示在序列A中前i个字母和序列B中前j个字母的公共子序列集合的max
状态转移,分情况讨论(dp的核心思想,将问题分成若干类,使得一个数可以表示一个集合),这里可以基于钱一个状态进行考虑,1.a[i-1]=b[j] , 2.a[i]=b[j].3,a[i]=b[j-1].4.a[i-1]=b[i-1]分别可以用一个max取尽四种情况
f[i][j]=max(f[i-1][j],f[i-1][j-1]+1(a[i]=b[j]),f[i][j-1].f[i-1][j-1])
图
#include
#include
using namespace std;
const int N=1010;
int f[N][N];
char a[N],b[N];
int main()
{
int n,m;
cin>>n>>m>>a+1>>b+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
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][m];
return 0;
}
AcWing 902. 最短编辑距离
给定两个字符串 A和 B,现在要将 A经过若干操作变为 B,可进行的操作有:
删除–将字符串 A 中的某个字符删除。插入–在字符串 A 的某个位置插入某个字符。
替换–将字符串 A 中的某个字符替换为另一个字符。
现在请你求出,将 A 变为 B至少需要进行多少次操作。
输入格式
第一行包含整数 n,表示字符串 A 的长度。
第二行包含一个长度为 n 的字符串 A。
第三行包含整数 m,表示字符串 B的长度。
第四行包含一个长度为 m 的字符串 B。
字符串中均只包含大小写字母。
输出格式
输出一个整数,表示最少操作次数。
数据范围
1≤n,m≤1000
输入样例:
10
AGTCTGACGC
11
AGTAAGTAGGC
输出样例:
4
思路:用f[i,j]表示A的前i个字母变成B前j个字母所需要的操作的最小值,状态转移:
f[i][j]=min(删f[i-1,j]+1 插f[i,j-1]+1 换f[i-1,j-1]+1 不变f[i-1][j-1]).记得初始化状态
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
char a[N],b[N];
int f[N][N];
int main()
{
int n,m;
cin>>n>>a+1>>m>>b+1;
for(int i=1;i<=n;i++) f[i][0]=i;
for(int i=1;i<=m;i++) f[0][i]=i;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
f[i][j]=min(f[i-1][j],f[i][j-1])+1;
if(a[i]==b[j]) f[i][j]=min(f[i][j],f[i-1][j-1]);
else f[i][j]=min(f[i][j],f[i-1][j-1]+1);
}
cout<<f[n][m];
return 0;
}
AcWing 899. 编辑距离
给定 n 个长度不超过 10 的字符串以及 m次询问,每次询问给出一个字符串和一个操作次数上限。
对于每次询问,请你求出给定的 n个字符串中有多少个字符串可以在上限操作次数内经过操作变成询问给出的字符串。
每个对字符串进行的单个字符的插入、删除或替换算作一次操作。
输入格式
第一行包含两个整数 n和 m。
接下来 n 行,每行包含一个字符串,表示给定的字符串。
再接下来 m 行,每行包含一个字符串和一个整数,表示一次询问。
字符串中只包含小写字母,且长度均不超过 10。
输出格式
输出共 m 行,每行输出一个整数作为结果,表示一次询问中满足条件的字符串个数。
数据范围 1≤n,m≤1000
输入样例:
3 2
abc
acd
bcd
ab 1
acbd 2
输出样例:
1
3
思路:此题只需将数据存储下来调上一个方法的实现就可以了
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=15,M=1010;
char g[M][N];
int f[N][N];
int count(char a[],char b[])
{
int n=strlen(a+1),m=strlen(b+1);
for(int i=1;i<=n;i++) f[i][0]=i;
for(int i=1;i<=m;i++) f[0][i]=i;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
f[i][j]=min(f[i-1][j],f[i][j-1])+1;
if(a[i]==b[j]) f[i][j]=min(f[i][j],f[i-1][j-1]);
else f[i][j]=min(f[i][j],f[i-1][j-1]+1);
}
return f[n][m];
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++) cin>>g[i]+1;
while(m--)
{
char b[N];
int LimitLen,s=0;
cin>>b+1>>LimitLen;
memset(f,0,sizeof f);
for(int i=0;i<n;i++)
if(count(g[i],b)<=LimitLen) s++;
cout<<s<<endl;
}
return 0;
}