数列4
题目描述
虽然蔡书苹长大了,但她还是很喜欢找点游戏自娱自乐。有一天,她在纸上写了一串数字:1,1,2,5,4。接着她擦掉了一个1,结果发现剩下1,2,4都在自己所在的位置上,即1在第1位,2在第2位,4在第4位。她希望擦掉某些数后,剩下的数列中在自己的位置上的数尽量多。她发现这个游戏很好玩,于是开始乐此不疲地玩起来…不过她不能确定最后能有多少个数在自己的位置上,所以找到你,请你帮忙计算一下!
输入格式
第一行为一个数n,表示数列的长度。
接下来一行为n个用空格隔开的正整数,第i行表示数Ai。
输出格式
一行,一个整数,表示擦掉某些数后,最后剩下的数列中最多能有多少个数在自己的位置上,即Ai=i最多能有多少。
输入样例
5
1 1 2 5 4
输出样例
3
解题思路
本题大意:求一个序列删除若干数后,使得剩下的数与自己所在位置相等的个数最多。
通过观察题目,我们可以对 A A A数列中的数进行分类讨论,设 p o s i pos_i posi为 A i A_i Ai目前所在的位置:
- 若 A i > p o s i A_i>pos_i Ai>posi,那么无论怎么删数, A i A_i Ai都不可能到达它原来的位置。
- 若 A i = p o s i A_i=pos_i Ai=posi,那么不用删数, A i A_i Ai就在它原来的位置上。
- 若 A i < p o s i A_i<pos_i Ai<posi,那么删去若干个数, A i A_i Ai就会在它原来的位置上。
综上所述,我们只需考虑第2,第3种情况,并且发现最后到达自己原来位置的数呈现出单调上升的趋势,因此我们考虑使用动态规划,由此本题正解很明显是线性动态规划。
我们设 f [ i ] f[i] f[i]表示以 A i A_i Ai结尾,前面所能组成的最长的答案数列。那么 A i A_i Ai可以作为前面其中一些合法数列的结尾,我们设前面符合第2,第3条件的数为 A k 1 , A k 2 , A k 3 ⋯ A k x A_{k_1},A_{k_2},A_{k_3}\cdots A_{k_x} Ak1,Ak2,Ak3⋯Akx,但是并不是所有数列后面都能接 A i A_i Ai,举个例子:
i i i | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
A i A_i Ai | 5 | 1 | 2 | 4 | 6 | 3 |
在这个数列中 A 2 , A 3 , A 4 A_2,A_3,A_4 A2,A3,A4都是可以回到原来的位置的,如果以 A 4 A_4 A4为某个合法数列的结尾,那么这个合法数列最长为1,就是他自己。因为 A 4 A_4 A4如果接在 A 2 , A 3 A_2,A_3 A2,A3的后面,那么当这两个数回到原来位置后, A 4 A_4 A4就会变成第1种情况,变得不合法。
由此我们知道,如果以 A i A_i Ai为结尾,那么设它可以接在 A k j A_{k_j} Akj后面, A k j A_{k_j} Akj与 A i A_i Ai满足以下条件:
- A k j < A i A_{k_j}<A_i Akj<Ai
- p o s i − p o s k j ≥ A i − A k j pos_i-pos_{k_j}\geq A_i-A_{k_j} posi−poskj≥Ai−Akj
状态转移方程: f [ i ] = m a x ( f [ i ] , f [ k j ] + 1 ) f[i]=max(f[i],f[k_j]+1) f[i]=max(f[i],f[kj]+1)
代码
/*
1.a[i]必须在i或i之后才有可能到达原来的位置
2.若最长数列里有a[i]和a[j]且i<j,那么有a[i]<a[j] 且 a[j]-a[i](数值上的差)<=j-i(位置上的差) 且 a[i]!=a[j]
*/
#include<iostream>
using namespace std;
int n;
int a[1005],f[1005],ans;
/*
f[i]表示以a[i]结尾所得的最长序列
根据以上条件可得
f[i]=max(f[i],f[k1]+1,f[k2]+1...,f[kn]+1) (kn<i&&a[i]>a[kn]&&a[i]!=a[kn]&&a[i]-a[kn]<=i-kn)
*/
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
if(a[i]<=i)
{
f[i]=1;
for(int j=1;j<i;j++)
if(a[j]<=j&&(i-j>=a[i]-a[j])&&a[i]>a[j])
f[i]=max(f[i],f[j]+1);
}
for(int i=1;i<=n;i++)
ans=max(f[i],ans);
cout<<ans;
return 0;
}