湖南省第12届大学生计算机程序设计大赛 最长上升子序列 csu 1807

这篇博客介绍了湖南省第12届大学生计算机程序设计大赛中的一道题目——最长上升子序列。文章详细阐述了题目的描述、输入、输出格式,并给出了样例输入和输出,同时提供了解题提示和题目来源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1807: 最长上升子序列~

            Time Limit: 5 Sec       Memory Limit: 128 Mb       Submitted: 276       Solved: 52    

Description

Bobo 在 ICPCCamp 学会了解决最长上升子序列问题后得到了一个长度为 n 的数列 p 1,p 2,…,p n.
Bobo 想用 1,2,…,n 来替换其中值为 0 的元素,使得 p 1,p 2,…,p n 互不相同(即 p 1,p 2,…,p n 是 {1,2,…,n} 的排列)。
现在 Bobo 想知道,替换后最长上升子序列的长度恰好为 (n-1) 数列的数量。

Input

输入包含不超过 300 组数据,其中不超过 20 组的 n 超过 100.
每组数据的第一行包含一个整数 n (1≤n≤10 5).
第二行包含 n 个整数p 1,p 2,…,p n  (0≤p i≤n).
保证p 1,p 2,…,p n中非 0 的元素互不相同。

Output

对于每组数据,输出一个整数表示要求的值。

Sample Input

3
0 0 0
4
0 0 0 0
5
1 0 0 4 5

Sample Output

4
9
1

Hint

Source

湖南省第十二届大学生计算机程序设计竞赛

#include<bitset>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, a[N];
long long dp[N];

void init()
{
    dp[0] = dp[1] = 0;
    for(int i=2; i<=N-1; i++)
    {
        dp[i] = dp[i - 1] + i-1+i-1;
        //dp[i]表示在长度为i的时候,最长上升子序列的长度等于i-1的次数,同时也可以这么理解:数列的长度为n,
        //数列按从小到大的顺序排列,要求最长上升子序列的长度为n-1的次数那我们每次只移动一个数字,使得只有一个数错位
        //即考虑第i个数放置的位置,1.当第i个数放在第i的位置的时候,dp[i]=dp[i-1];2.当第i个数放在第i-1的位置上的时候
        //第i的位置就有i-1种方法(前面i-1个数都可以放在第i位上,就只有i上的数字是错位的)3.当第i个数放在其他位置上的时候,有i-2种方法
        //当i放在前面i-2的任何位置的时候,除了i放的那个位置,其余位置的数都必须从小到大排序,这样才能保证只有一个数错位
        //第i个后面的数不用管,因为会按顺序计算下去;
    }
}

int check(int x)//若偏差大于1
{
    int now = 1;
    for(int i=1;i<=n;i++)
    {
        if (now == a[x]) ++now;//如果now==a[x],意味着偏差开始或者结束
        if (i == x) continue;//这个时候说明x这个位置上的数要比他后面的数要大,若now再加1的话就会满足下面条件,使得输出值错误
        if (a[i] && a[i] != now) return 0;//当a[i]!=0并且a[i]!=now,那么说明该数列不能成为最长上升子序列的值为n-1;
        ++now;
    }
    return 1;
}

int check(int x, int y)//前后两个数之间对换了位置,即前进又后退的情况
{
    if (x != y - 1) return 0;
    for(int i=1;i<=n;i++)
    {
        if (i == x || i == y) continue;
        if (a[i] && a[i] != i) return 0;//12435768这种情况需要这样判断
    }
    return 1;
}

long long  get(int x, int y)//只前进或者只后退的情况
{
    int l = n + 1, r = 0;
    for(int i=1; i<=n; i++)
    {
        if (a[i] && a[i] != i)//记录错位的数的位置
        {
            l = min(l, i);//记录最开始错位的位置
            r = max(r, i);//记录最后错位的位置
        }
    }
    for(int i=l;i<=r; i++)
    {
        if (a[i] && a[i] == i)//如果在错位的这一段数中有没有错位的,就说明最长上升子序列不可能为n-1,或者不满足p1,p2,…,pn中非 0 的元素互不相同。
        {
            return 0;
        }
    }
    int L = l, R = n - r + 1;
    for(int i=l; i>=1; i--)
    {
        if (a[i] == i)
        {
            L = l - i;//计算前面有几个0,也就是说记录有几个可以活动的位置
            printf("%d#\n",L);
            break;
        }
    }
    for(int i=r; i<=n; i++)
    {
        if (a[i] == i)
        {
            R = i - r;//计算后面有几个0,也就是说记录有几个可以活动的位置
            printf("%d*\n",R);
            break;
        }
    }

    return 1LL * (L - x) * (R - y);//* 1LL是为了在计算时,把int类型的变量转化为long long,然后再赋值给long long类型的变量
    //前面有n个0,那么可以变换的方式就有n+1种,后面的是n个0就可以变换n种,因为最长上升子序列的长度必须为n-1。
}

long long get()//没有错位的情况,全靠0来变换
{
    long long  res = 0, now = 0;
    for(int i=1; i<=n; i++)
    {
        if (a[i])
        {
            res += dp[a[i] - now - 1];
            now = a[i];
        }
    }
    return res + dp[n - now];
}

int main()
{
    init();
    while (scanf("%d", &n) != EOF)
    {
        int flag = 0, l = 0, r = 0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d", &a[i]);
            if (!a[i]) continue;//a[i]=0;
            if ( abs(a[i] - i)> 1) flag = i;//abs是绝对值函数,答案只有一种或者没有
            if (a[i] - i == 1) l = i;
            if (i - a[i] == 1) r = i;
        }
        if (flag)
        {
            printf("%d\n", check(flag));
            continue;
        }
        if (l&&r)
        {
            printf("%d\n", check(l, r));
            continue;
        }
        if (l || r)
        {
            printf("%lld\n", get(!l, !r));
            continue;
        }
        printf("%lld\n", get());
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值