线性DP入门的三道经典题思路这里就不多说了,直接上代码:
数字三角形:
题目链接:戳
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int N = 500+10; int n; int a[N][N]; int main() { memset(a,-0x1f,sizeof(a)); scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) { scanf("%d",&a[i][j]); if(i>1)a[i][j]+=max(a[i-1][j],a[i-1][j-1]); } int maxx=-2e+9; for(int i=1;i<=n;i++)maxx=max(maxx,a[n][i]); printf("%d ",maxx); return 0; }
LIS问题:
题目链接:戳
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; const int N = 1000+10; int n; int a[N],f[N]; int main() { scanf("%d",&n); int maxx=a[0]=-2e+9; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); for(int j=0;j<i;j++) if(a[j]<a[i])f[i]=max(f[i],f[j]+1); maxx=max(maxx,f[i]); } printf("%d ",maxx); return 0; }
LCS问题:
题目链接:戳
#include<bits/stdc++.h> using namespace std; char sa[2010],sb[2010]; int f[2010][2010]; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'|| ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } int main() { int la=read(),lb=read(); scanf("%s%s",sa+1,sb+1); for(int i=1;i<=la;i++) for(int j=1;j<=lb;j++) { f[i][j]=max(f[i-1][j],f[i][j-1]); if(sa[i]==sb[j])f[i][j]=max(f[i][j],f[i-1][j-1]+1); } printf("%d ",f[la][lb]); return 0; }
LIS问题二分算法
题目链接:戳
这里要讲的是LIS问题的二分+贪心算法,可以将时间复杂度从O(n²)降低为O(nlogn)
[算法思路]
用数组b存储序列a的最长上升子序列,同时记录b的长度len,并用以下方法维护b数组:
枚举a[i],若a[i]>b[len],则直接把a[i]接到b数组后面;否则在b数组中查找第一个大于等于a[i]的数并替换之。由于b数组具有单调性,故可以使用二分查找。
[代码]
#include<cstdio> #include<algorithm> using namespace std; const int N=100000+10; int a[N],b[N]; int binary_search(int l,int r,int num) { while(l<r) { int mid=(l+r)>>1; if(b[mid]<num)l=mid+1; else r=mid; } return l; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } int main() { int n=read(),len=1; b[1]=a[1]=read(); for(int i=2;i<=n;i++) { if((a[i]=read())>b[len])b[++len]=a[i]; else b[binary_search(1,len,a[i])]=a[i]; } printf("%d ",len); return 0; }
编辑距离
再介绍一道“编辑距离”问题:
题目链接:戳
DP思路:对于两个字符串A和B,我们仍然是维护二维的F数组进行DP。F[i,j]表示到字符串A的i位置和字符串B的j位置为止的最小编辑距离。为了得到F[i,j],我们有六种操作,分别是:在Ai-1后面插入字符Bi,将Bj字符删除;将Ai字符删除,在Bi-1后面插入字符Ai;将Ai改成Bj,将Bj改成Ai(仅限于Ai≠Bj时)。显然,这六种操作中,第一和二,三和四,五和六种操作都是等价的,故可以合并为三种。于是我们便可以得到状态转移方程:
F[i,j]=min{F[i-1][j]+1,F[i][j-1]+1,F[i-1][j-1](if Ai==Bj),F[i-1][j-1]+1(if Ai!=Bj)}
边界:F[i][0]=F[0][j]=0;目标:F[la][lb]
代码如下:
#include<iostream> #include<algorithm> #include<cstring> #define N 1000+10 using namespace std; int f[15][15]; char sa[N][15],sb[15]; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'|| ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } int main() { int n=read(),m=read(); for(int i=1;i<=n;i++)scanf("%s",sa[i]+1); for(int k=1;k<=m;k++) { scanf("%s",sb+1); int num=read(),sum=0; for(int q=1;q<=n;q++) { int la=strlen(sa[q]+1),lb=strlen(sb+1); for(int i=0;i<=la;i++)f[i][0]=i; for(int i=0;i<=lb;i++)f[0][i]=i; for(int i=1;i<=la;i++) for(int j=1;j<=lb;j++) { f[i][j]=min(f[i-1][j]+1,f[i][j-1]+1); f[i][j]=min(f[i][j],f[i-1][j-1]+1); if(sa[q][i]==sb[j]) f[i][j]=min(f[i][j],f[i-1][j-1]); } if(f[la][lb]<=num)sum++; } printf("%d ",sum); } return 0; }