题目描述
数列(线性DP好题)
虽然msh长大了,但她还是很喜欢找点游戏自娱自乐。有一天,她在纸上写了一串数字:1,l,2,5,4。接着她擦掉了一个l,结果发现剩下l,2,4都在自己所在的位置上,即1在第1位,2在第2位,4在第4位。她希望擦掉某些数后,剩下的数列中在自己位置上的数尽量多。她发现这个游戏很好玩,于是开始乐此不疲地玩起来……不过她不能确定最多能有多少个数在自己的位置上,所以找到你,请你帮忙计算一下!
输入格式
第一行为一个数n,表示数列的长度。
接下来一行为n个用空格隔开的正整数,第i行表示数Ai。
输出格式
一行一个整数,表示擦掉某些数后,最后剩下的数列中最多能有多少个数在自己的位置上,即Ai=i最多能有多少。
输入输出样例
输入 #1
5
1 1 2 5 4
输出 #1
3
说明/提示
对于20%的数据,n≤20;
对于60%的数据,n≤100;
对于100%的数据,n≤l000.
思路
DP的码量很小,但个人认为思维难度仅次于数论,而这个DP基础题我都是费了很大劲才A了,还看了题解.
本题很容易想到用二维状态
f
[
i
]
[
j
]
{f[i][j]}
f[i][j]来表示前
i
{i}
i个数中删除
j
{j}
j个后的符合条件数的个数的最大值.那么根据题意我们到第
i
{i}
i个数时有两种选择:
第一种:不选直接删,则有DP转移方程:
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
1
]
{f[i][j]=f[i-1][j-1]}
f[i][j]=f[i−1][j−1];
第二种:选择加入这个数,那么又有两种情况:
若(
a
[
i
]
=
i
−
j
{a[i]=i-j}
a[i]=i−j(即为符合要求的数))则有
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
−
1
]
,
f
[
i
−
1
]
[
j
]
+
1
)
{f[i][j]=max(f[i-1][j-1],f[i-1][j]+1)}
f[i][j]=max(f[i−1][j−1],f[i−1][j]+1),
否则:
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
−
1
]
,
f
[
i
−
1
]
[
j
]
)
{f[i][j]=max(f[i-1][j-1],f[i-1][j])}
f[i][j]=max(f[i−1][j−1],f[i−1][j])。
最后在循环中添加
a
n
s
{ans}
ans统计最大值即可.
代码
#include<bits/stdc++.h>
#define N 1005
using namespace std;
int n,ans=-1;
int a[N],f[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++)//预处理,避免在两重循环中特判f[i-1][j-1]
{
if(a[i]==i)f[i][0]=f[i-1][0]+1;
else f[i][0]=f[i-1][0];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)//这里我简化了一点点
{
if(a[i]==i-j)f[i][j]=max(f[i-1][j-1],f[i-1][j]+1);
else f[i][j]=max(f[i-1][j-1],f[i-1][j]);
ans=max(f[i][j],ans);
}
printf("%d\n",ans);
return 0;
}