一看这道题就想到DP…但是我错误地认为当时的DP思路有后效性,没有敢打,最后改装了一下最长不降子序列,竟然对了~
【问题描述】
虽然msh长大了,但他还是和喜欢找点游戏自娱自乐。有一天,他在纸上写了一串数字: 1 , 1 , 2 , 5 , 4 。接着他擦掉了一个1,结果发现剩下1, 2 ,4都在自己所在的位置上,即1在第1位,2在第2位,4在第4位。他希望擦掉某些数后,剩下的数列中在自己位置上的尽量多。他发现这个游戏很好玩,于是开始乐此不疲地玩起来……不过他不能确定最多能有多少个数在自己的位置上,所有找到你,请你帮忙计算一下 !
【输入格式】
第1行:1个整数n,表示数列的长度。接下来一行为n个空格隔开的正整数,第i行表示Ai。
【输出格式】
一行一个整数,表示擦掉某些数后,最后剩下的数列中最多能有多少个数在自己的位置上,即Ai = i最多能有多少。
【样例输入】
5
1 1 2 5 4
【样例输出】
3
【数据范围】
对于20 % 的数据,n <= 20 ;
对于60 % 的数据,n <= 100 ;
对于100 % 的数据,n <= 1 , 000 。
两种方法。第一种是改装的最长不将子序列。我们将每个数和自己应该在的位置的差记录下来,然后就得到一个新的数列d。我们对这个数列做一下最长不将子序列,但是要注意一点,一定要有数可删。什么意思呢,用数学语言描述就是j-i>d[j]-d[i](j>i),即在i确定的情况下,从i到j之间至少要有d[j]-d[i]个数以保证可以通过删数让j到它自己的位置上。
第二种就是普通的DP。思路、方程、代码详见SephirothLee的《数列》一文,地址 http://www.cnblogs.com/sephirothlee/archive/2010/10/08/1846073.html
参考代码(最长不降子序列):
1 program sequence;
2 var
3 n,max,i,j,l:longint;
4 a,d: array [ 0 .. 1001 ] of longint;
5 f: array [ 0 .. 1001 ] of longint;
6 ff:boolean;
7 begin
8 readln(n);
9 for i: = 1 to n do
10 begin
11 read(a[i]);
12 d[i]: = i - a[i]; // 不能写成i - a[i],否则处理方法不同
13 end ;
14 for i: = n - 1 downto 1 do
15 for j: = i + 1 to n do
16 if (d[i] >= 0 ) and (d[j] >= 0 ) and (d[i] <= d[j]) then // 如果是负数,就不能通过删数归位
17 if d[j] - d[i] <= j - i - 1 then // 关键的限制条件
18 if (d[i] < i) and (d[j] < j) then
19 if f[j] + 1 > f[i] then f[i]: = f[j] + 1 ;
20 ff: = false;
21 for i: = 1 to n do
22 if max < f[i] then
23 begin
24 ff: = true;
25 l: = i;
26 max: = f[i];
27 end ;
28 if (d[l] > l) or (d[l] < 0 ) then dec(max); // 一定要保证第一个数可以归位
29 if ff then writeln(max + 1 ) // 加上最后面的一个
30 else writeln( ' 0 ' ); // 如果根本就没有,则输出0
31 end .
(saltless原创,转载请注明出处)