本人的第四篇博客,一早起来发现有7粉丝了!还挺意外的;
荣幸能够帮到大家,祝.好运连连~
问题一 回文词
例题 https://www.luogu.com.cn/problem/U454939
首先,对于任意一个字符串,我们该怎么知道它是不是回文词呢?
如 :ACDBA
不妨把它倒着写一遍
变成:ABDCA
发现了吗,正着写和倒着写不一样,不是回文词
那该怎么改呢?
对于ACDBA来说,C和B出了问题,就像下面:
ACDBA
ABDCA
我们可以在前面、后面分别加上B、C,让它变成这样:
ABCDCBA
ABCDCBA
不难发现,我们不需要添加的部分就是他们的公共子序列,又因要求最少,所以就是两个序列的最长公共子序列(神奇吧,又和DP扯上关系了)
既然这样,那需要添加的就是最长公共子序列以外序列的长度了
解决~
代码:
#include<bits/stdc++.h>
using namespace std;
long long n,ans,f[5001][5001];
char a[5001],b[5001];
int main(){
scanf("%lld", &n);
for(int i=1;i<=n;i++){
scanf(" %c", &a[i]);
b[n-i+1]=a[i];
}
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(a[i]==b[j]) f[i][j]=f[i-1][j-1]+1;
else f[i][j]=max(f[i-1][j],f[i][j-1]);
}
}
printf("%lld\n", n-f[n][n]);
}
问题二 拦截导弹
P1020 [NOIP1999 提高组] 导弹拦截 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
翻译一下,就是让你求最长上升子序列,不一样的是,在中间还要顺手求一下最少的数量
将拦截的导弹的高度提出来成为原高度序列的一个子序列,根据题意这个子序列中的元素是单调不增的(即后一项总是不大于前一项),我们称为单调不升子序列。本问所求能拦截到的最多的导弹,即求最长的单调不升子序列。
记 dp[i] 表示「对于前 i 个数,在选择第i 个数的情况下,得到的单调不升子序列的长度最长是多少」。于是可以分两种情况(这里演示的是一维代码):
- 第 i 个数是子序列的第一项。则
- 第 i 个数不是子序列的第一项。选择的第 i 个数之前选择了第 j 个数。
- 根据题意,第 j 个数的值 h(j) 应当小于第 i 个数的值 h(i)。枚举这样的 j,可以得到状态转移方程:
综合这两种情况,得到最终的状态转移方程:
上代码:
#include<bits/stdc++.h>
#define up(l,r,i) for(int i=l,END##i=r;i<=END##i;++i)
#define dn(r,l,i) for(int i=r,END##i=l;i>=END##i;--i)
using namespace std;
typedef long long i64;
const int INF =2147483647;
const int MAXN=1e5+3;
int n,t,H[MAXN],F[MAXN];
int main(){
while(~scanf("%d",&H[++n])); --n;
t=0,memset(F,0,sizeof(F)),F[0]=INF;
up(1,n,i){
int l=0,r=t+1; while(r-l>1){
int m=l+(r-l)/2;
if(F[m]>=H[i]) l=m; else r=m;
}
int x=l+1; // dp[i]
if(x>t) t=x; F[x]=H[i];
}
printf("%d\n",t);
t=0,memset(F,0,sizeof(F)),F[0]=0;
up(1,n,i){
int l=0,r=t+1; while(r-l>1){
int m=l+(r-l)/2;
if(F[m]<H[i]) l=m; else r=m;
}
int x=l+1;
if(x>t) t=x; F[x]=H[i];
}
printf("%d\n",t);
return 0;
}