2023.2.9(总结)

今天还是动态规划,我都快被动态规划搞心态了。

P1091 [NOIP2004 提高组] 合唱队形

题目描述

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

合唱队形是指这样的一种队形:设 kk 位同学从左到右依次编号为 1,2,1,2, … ,k,k,他们的身高分别为 t_1,t_2,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 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入格式

共二行。

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

第二行有 nn 个整数,用空格分隔,第 ii 个整数 t_iti​(130\le t_i\le230130≤ti​≤230)是第 ii 位同学的身高(厘米)。

输出格式

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

输入输出样例

输入 #1复制

8
186 186 150 200 160 130 197 220

输出 #1复制

4

说明/提示

对于 50\%50% 的数据,保证有 n \le 20n≤20。

对于全部的数据,保证有 n \le 100n≤100。

分析:

1,从右往左,按左高右低顺序找出每一个位置右边有几个从高到低的数,即为b[i](包括自己).

2,从左往右,按左低右高顺序找出每一个位置左边有几个从低到高的数,即为b[i](包括自己).

3,接着就可以把自己左边的从低到高的数和右边从高到低的数相加,记得减一。

代码如下:

#include<stdio.h>
#include<math.h>
#include<string.h>
int n,a[105],b[105],c[105],m;

int max(int x,int y)
{
    return x>y?x:y;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",a+i);
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<i;j++)
        {
            if(a[i]>a[j])
            b[i]=max(b[i],b[j]+1);
        }
    }
    for(int i=n;i>0;i--)
    {
        for(int j=n+1;j>i;j--)
        {
            if(a[i]>a[j])
            c[i]=max(c[i],c[j]+1);
        }
    }
    for(int i=1;i<=n;i++)
    {
        m=max(b[i]+c[i]-1,m);
    }
    printf("%d",n-m);
}

P1439 【模板】最长公共子序列

题目描述

给出 1,2,\ldots,n1,2,…,n 的两个排列 P_1P1​ 和 P_2P2​ ,求它们的最长公共子序列。

输入格式

第一行是一个数 nn。

接下来两行,每行为 nn 个数,为自然数 1,2,\ldots,n1,2,…,n 的一个排列。

输出格式

一个数,即最长公共子序列的长度。

输入输出样例

输入 #1复制

5 
3 2 1 4 5
1 2 3 4 5

输出 #1复制

3

说明/提示

  • 对于 50\%50% 的数据, n \le 10^3n≤103;
  • 对于 100\%100% 的数据, n \le 10^5n≤105。

分析:

1,这个题目我之前做过一次,如果我没有记错的话,但是它的数据比我之前做过的那个题目要卡的严格一些。

2,用dp[i]​记录以ii为长度的最长上升子序列的末尾元素的最小值,以l1记录目前的最长上升子序列,这样可以保证dp数组单调递增。

3,然后我们通过二分法找位置就行。

代码如下:

#include<stdio.h>
#include<math.h>
#include<string.h>
int n,n1,a[100005],b[100005],dp[100005];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&n1);
        a[n1]=i;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",b+i);
        b[i]=a[b[i]];
    }
    int l1,l,r,m;
    l1=1;
    dp[1]=b[1];
    for(int i=2;i<=n;i++)
    {
        if(dp[l1]<b[i])
        dp[++l1]=b[i];
        else
        {
            l=1;r=l1;
            while(l<r)
            {
                m=(l+r)/2;
                if(dp[m]>b[i])
                r=m;
                else 
                l=m+1;
            }
            dp[l]=b[i];
        }
    }
    printf("%d",l1);
}

总结:

动态规划不容易啊,是我一直以来的一个瓶颈,遇到难点的题目就写不出来了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值