寒假笔记·最长升序列与最长公共子序列

最长升序列

例题:最少拦截系统

原题地址
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.
怎么办呢?多搞几套系统呗!你说说倒蛮容易,成本呢?成本是个大问题啊.所以俺就到这里来求救了,请帮助计算一下最少需要多少套拦截系统.
Input
输入若干组数据.每组数据包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔)
Output
对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统.
Sample Input
8 389 207 155 300 299 170 158 65
Sample Output
2

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,a[100010],d[100010];
int main()
{    
	int i,j,t,p;
	while(scanf("%d",&n)!=EOF)
	{
		t=1;
		memset(d,0,sizeof(d));
		for(i=1;i<=n;i++)
			scanf("%d",&a[i]);
		d[1]=a[1];
		for(i=2;i<=n;i++)
		{
			if(a[i]>d[t]) d[++t]=a[i];//建立动态数组
			else 
			{
				j=lower_bound(d+1,d+t+1,a[i])-d;//在动态数组中寻找位置
				d[j]=a[i];//改变
			}
		}
		printf("%d\n",t);
	}
    return 0;
}

例题:P1091 合唱队形

原题地址
题目描述

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。

合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2,…,K,他们的身高分别为T_1,T_2,…,T_K , 则他们的身高满足T_1<…<T_i>T_{i+1}>…>T_K(1 <= i <= K)
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入输出格式

输入格式:
共二行。

第一行是一个整数N(2≤N≤100),表示同学的总数。

第二行有n个整数,用空格分隔,第i个整数T_i (130≤T_i ≤230)是第i位同学的身高(厘米)。

输出格式:
一个整数,最少需要几位同学出列。

输入输出样例

输入样例#1:
8
186 186 150 200 160 130 197 220
输出样例#1:
4
说明

对于50%的数据,保证有n≤20;

对于全部的数据,保证有n≤100。
代码:
我们先看从T1到Ti这一段单调递增的序列,再看Ti到TK这一段单调递减的序列,那么问题就解决了。先从1到n求一趟最长升,然后从n到1也求一趟,最后枚举中间的Ti,然后从众多Ti中挑个大的。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
int num[1010];
int ans=1;
int sum1[1010],sum2[1010];
int main()
{
    int n,i,j;
    cin>>n;
    for(i=1;i<=n;i++)
{
      cin>>num[i];
      sum1[i]=1;
      sum2[i]=1;
}
    for(i=1;i<=n;i++)
      for( j=1;j<i;j++)
        if(num[i]>num[j])
        sum1[i]=max(sum1[i],sum1[j]+1);//求在升序列中的位置
    for(i=n;i>=1;i--)
      for( j=n;j>i;j--)
        if(num[i]>num[j])
        sum2[i]=max(sum2[i],sum2[j]+1);//求在降序列中的位置
    for(i=1;i<=n;i++)
        ans=max(ans,(sum2[i]+sum1[i]-1));//求当i为最高点时的情况
    cout<<n-ans<<endl;
    return 0;
}

LCS:最长公共子序列

例题:P2543 [AHOI2004]奇怪的字符串

原题地址

题目描述
在这里插入图片描述
输入输出格式

输入格式:
输入文件中包含两个字符串X和Y。当中两字符串非0即1。序列长度均小于9999。

输出格式:
X和Y的最长公共子序列长度。

输入输出样例

输入样例#1:
01010101010 00000011111
输出样例#1:
6
输入样例#2:
01011 010010101111111111
输出样例#2:
5
代码:
这道题就是LCS的模板,状态转移方程就是dp[i][j]=(a[i-1]==b[j-1])?dp[i-1][j-1]:max(dp[i-1][j],dp[i][j-1])

其中,i代表串a已经计算到了第i个字符,j代表串b已经计算到了第j个字符,dp[i][j]代表a的前i个字符与b的前j个字符的LCS。

所以,如果这两个字符相等,那么直接继承dp[i-1][j-1];

反之,则在dp[i-1][j],dp[i][j-1]中选取最大的数。

为了优化一下(毕竟ab两串最大有10000个字符),所以我们可以想到滚动数组。

dp[i][j]只与上一行有关联,所以上面的那个方程可以写成if (a[i-1]==b[j-1]) dp[i%2][j]=dp[(i-1)%2][j-1]+1 else dp[i%2][j]=max(dp[(i-1)%2][j],dp[i%2][j-1])。

AC代码:

#include<iostream> 
#include<cstdio> 
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
using namespace std; 
int qq[2][10010];
char ch[10010],sh[10010];
int main()
{
    int n,m,i,j;
    scanf("%s",ch+1);
    scanf("%s",sh+1);
    n=strlen(ch+1);
    m=strlen(sh+1);
    memset(qq,0,sizeof(qq));
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
        {
            if(ch[i]==sh[j]) qq[i%2][j]=qq[(i-1)%2][j-1]+1;
            else qq[i%2][j]=max(qq[i%2][j-1],qq[(i-1)%2][j]);
        }
    printf("%d\n",qq[n%2][m]);
    return 0;
}

例题:P1435 回文字串

原题地址

题目描述

回文词是一种对称的字符串。任意给定一个字符串,通过插入若干字符,都可以变成回文词。此题的任务是,求出将给定字符串变成回文词所需要插入的最少字符数。

比如 “Ab3bd”插入2个字符后可以变成回文词“dAb3bAd”或“Adb3bdA”,但是插入少于2个的字符无法变成回文词。

注:此问题区分大小写

输入输出格式

输入格式:
一个字符串(0<strlen<=1000)

输出格式:
有且只有一个整数,即最少插入字符数

输入输出样例

输入样例#1:
Ab3bd
输出样例#1:
2

代码
解题思路:该题说是考察如何将一个字符串添加成一个回文串的,不如说是一道求最长公共自序列的变式题。
我们先分析下样例:ab3bd,
它的倒序是:db3ba
它可以这么理解,正序与倒序“公共”的部分就是我们回文的部分,如果把正序与倒序公共的部分减去你就会惊奇的发现剩余的字符就是你所要添加的字符,也就是所求的正解,这样我们就可以把问题转化成了求最长公共自序列的问题。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
char a[1010],b[1010];
int n;
int dp[1010][1010];
int main()
{
    int i,j;
    scanf("%s",a+1);
    n=strlen(a+1);
    for(i=1;i<=n;i++)
        b[i]=a[n-i+1];
    memset(dp,0,sizeof(dp));
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            if(b[j]==a[i]) dp[i][j]=dp[i-1][j-1]+1;
            else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        }
    }
    printf("%d\n",n-dp[n][n]);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值