12.12知识整理

1.P1091 [NOIP2004 提高组] 合唱队形

题目描述

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

合唱队形是指这样的一种队形:设 k 位同学从左到右依次编号为 1,2,k,他们的身高分别为

t1​,t2​, … ,t_k,tk​,则他们的身高满足 t_1< \cdots <t_i>t_{i+1}>t1​<⋯<ti​>ti+1​> … >t_k(1\le i\le k)>tk​(1≤i≤k)。

你的任务是,已知所有 nn 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入格式

共二行。

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

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

———————————————————————————————————————————

思路:额.....由上面那一堆乱码我们就可以得出,就是合唱队是由低到高有由高到低,这就需要我们写两个最长连续上升子序列,这里必须是要严格上升的子序列这里是第二个相对于从右到左的子序列,所以那里面的循环应该是倒着开始的,所以要j>i在他的前面进行搜索,如果有比a[i]更小也就是a[i]>a[j]因为是从后向前,之后j就是一开始等于n后要>i之后再他相对来说前面进行寻找,他是要倒着寻找的因为所以要倒着循环,我们是以f记录最少要出去几个同学,最少肯定是1了。但是我们是从少像多,之后再从多向少(相对于从左到右来说)中间哪个同学被算了两次所以我们要加上他俩的最大值之后再减一,之后最后打擂台求最值,但是要记住咱们求的这个是最多有多少个学生在这里,所以最后要用 n-ans才可以

———————————————————————————————————————————

先给出上升子序列相应模板之后再给出这个题的答案

#include<bits/stdc++.h>
using namespace std;
const int N=5010;
int a[N],f[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for(int i=1;i<=n;i++)
    {
        f[i]=1;
        for(int j=1;j<i;j++)
        {
            if(a[i]>a[j])
            {
                f[i]=max(f[i],f[j]+1);
            }
        }
    }
    int res=0;
    for(int i=1;i<=n;i++)
    {
        res=max(res,f[i]);
    }
    cout<<res;
}

———————————————————————————————————————————

之后是这个题

#include<bits/stdc++.h>
using namespace std;
const int N=5010;
int f1[N],f2[N],a[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
       cin>>a[i];
    }
    for(int i=1;i<=n;i++)
    {
        f1[i]=1;
        for(int j=1;j<i;j++)
        {
            if(a[i]>a[j])
            {
                f1[i]=max(f1[i],f1[j]+1);
                
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        f2[i]=1;
        for(int j=n;j>i;j--)
        {
            if(a[i]>a[j])
            {
            f2[i]=max(f2[i],f2[j]+1);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,f1[i]+f2[i]-1);
        
    }
    cout<<n-ans;
}

———————————————————————————————————————————

2.

拦截导弹

描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数,导弹数不超过1000),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入

第一行是一个整数N(不超过1000),表示导弹数。

第二行包含N个整数,为导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数)。

输出

第一行:最多能拦截的导弹数;

第二行:要拦截所有导弹最少要配备的系统数。

———————————————————————————————————————————

动态规划+贪心,第一个很简单就是求一个导弹系统的最长上升子序列就可以,但是第二问比较困难了,因为涉及到贪心,所以我们去拦截一个200的我们现在有220,和400的系统。我们是要采用220的来拦截200的这样在之后400的还可以拦截更多更高的导弹,但如果是400去兰200的话。就被更新成200,遇到更高的就挡不住了要打擂台,先求出最小的哪一个,之后如果发现拦截不了再新加一套系统就行了之后再新设置拦截高度
——————————————————————————————————————————

#include <iostream>

using namespace std;
const int N=1010;
int a[N], ans[N], b[N]; //b[i]表示第i套系数所能拦截的导弹的高度
/*
状态ans[i]:表示到第i枚导弹的最大不上升子序列的长度
*/
int main()
{

    int n=0, Max=0;
    cin>>n;
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
    }

    ans[1]=1;
    //求最长不上升子序列
    for(int i=2; i<=n; i++)
    {
        int m=0;
        for(int j=1; j<i; j++)
        {
            if(a[i]<=a[j]&&ans[j]>m)
                m=ans[j];
        }
        ans[i]=m+1;
        if(ans[i]>Max)
            Max=ans[i];
    }

    int cnt=1;
    b[cnt]=a[1];//初始化第一套系统能拦截导弹的高度
    for(int i=2; i<=n; i++) //从第二颗导弹开始,枚举所有导弹
    {
        //找最小能拦截导弹的系统
        int pos=0, m=50000; //pos最小值位置,m拦截高度
        for(int j=1; j<=cnt; j++)
        {
            if(b[j]>=a[i]&&b[j]<m) //能够拦截,并且比最小值还小
            {
                m=b[j];
                pos=j;
            }
        }
        if(!pos)   //拦截不了
        {
            cnt++;//增加一套系统
            b[cnt]=a[i];//设置该系统的最初高度
        }
        else
            b[pos]=a[i]; //能拦截则更新系统的拦截高度
    }


    cout<<Max<<endl;
    cout<<cnt<<endl;
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值