洛谷 P1799 数列 题解

题目链接

洛谷 P1799 数列

Task 1

这是一道明显的删数问题,我们设 \(dp_{i,j}\) 表示前 \(i\) 个数中删 \(j\) 个后,前 \(i-j\) 个数(即原来前 \(i\) 个数)中在自己位置上的数的最大个数。则当当前数删时,\(dp_{i,j}=dp_{i-1,j-1}\);当当前数不被删时,还需考虑这个数是否在自己位置上,即 \([a_i=i-j]\)\([P]\) 表示艾弗森括号,真为 \(1\),假为 \(0\)),所以此时 \(dp_{i,j}=dp_{i-1,j}+[a_i=i-j]\)

综上,\(dp_{i,j}=\begin{cases}dp_{i-1,j}+[a_i=i-j] &j=0 \\\max{(dp_{i-1,j-1},dp_{i-1,j}+[a_i=i-j])} &j>0\end{cases}\)。代码如下,时间复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;

const int N=1e3+10;
int n;
int a[N],dp[N][N];

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) dp[i][j]=max(j>0?dp[i-1][j-1]:-1,dp[i-1][j]+(a[i]==i-j)); // 即状态转移方程
    }
    int ans=0;
    for (int i=1;i<=n;++i) ans=max(ans,dp[n][i]);
    printf("%d",ans);
    return 0;
}

Task 2:滚动数组

观察到 \(dp_{i,j}\) 的更新只与 \(dp_{i-1,j-1}\)(如果存在)及 \(dp_{i-1,j}\) 有关,所以可以滚动数组优化,具体过程略过,详见这篇题解

#include<bits/stdc++.h>
using namespace std;

const int N=1e3+10;
int n;
int a[N],dp[N];

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=i;j>=0;--j) dp[j]=max(j>0?dp[j-1]:-1,dp[j]+(a[i]==i-j));
    }
    int ans=0;
    for (int i=1;i<=n;++i) ans=max(ans,dp[i]);
    printf("%d",ans);
    return 0;
}
posted @ 2026-03-08 16:16  CodingJuRuo  阅读(4)  评论(0)    收藏  举报