【原题】【noip 2004 T3】【动态规划】合唱队型

问题

描述 Description

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1, 2, …, K,他们的身高分别为T1, T2, …, TK,则他们的身高满足T1 < T2 < … < Ti , Ti > Ti+1 > … > TK (1 <= i <= K)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入格式 Input Format

输入的第一行是一个整数N(2 <= N <= 100),表示同学的总数。第一行有n个整数,用空格分隔,第i个整数Ti(130 <= Ti <= 230)是第i位同学的身高(厘米)。

输出格式 Output Format

输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。

 

分析

这是个灰常经典的问题。我们有n2的算法,(这里上午写错了)先从左到右和从右到左分别求两次最长上升序列,然后再枚举中间的人,两次的f【k】的值-1就是有的人,最后用总人数减去这个值即可。

为什么一定要这么做,就是为了保证在产生的序列中包含ti这个元素。

反思

经典的问题,经典的模型。但是看上去比较难理解,关键是能看到题目的本质,找到切入点,转化问题。类似的问题还有 过河,导弹拦截……还有输出方案的buylow,总之,多积累,对模型有感觉。

有些时候需要但创造性,比如:给定一个序列让你求包含第k项的最长XX序列。乍一看没思路,其实只需要做预处理即可,将k之前的和之后的不满足条件的元素都删掉,这样就保证了k一定会在最优决策中出现

code

 

program liukee;
var
  a:array[1..1000] of longint;
  f1,f2:array[0..1000] of longint;
  n,i,k,ans,j:longint;
begin
  readln(n);
  for i:=1 to n do
    read(a[i]);
  for i:=1 to n do
  begin
	f1[i]:=1;
	f2[i]:=1;
  end;
  for i:=1 to n do
	for j:=i-1 downto 1 do
      if(a[j]<a[i])and(f1[i]<f1[j]+1)then f1[i]:=f1[j]+1;
  for i:=n downto 1 do
	for j:=i+1 to n do
	  if(a[j]<a[i])and(f2[i]<f2[j]+1)then f2[i]:=f2[j]+1;
  for k:=1 to n do
    if f1[k]+f2[k]-1>ans then ans:=f1[k]+f2[k]-1;
  writeln(n-ans);
end.

题外话

求最长xx序列的方程可以优化为nlogn的。用到了单调队列和二分查找的方法。

对于每个f[]的取值k,我们只需要保留f[t]=k的所有a[t]值中最小的,令d[k]:=min{a[t]};f[t]=k;

我们注意到决策区间d满足单调性。单调递增

设当前已产生的序列长度为len,先判断a[t]与d[len],如果a[t]>d[len],那么将a[t]接在d[len]后,得到一个更长的序列inc(len),d[len]:=a[t];。

否则在d[1]……d[len]中寻找最大的j满足d[j]<a[t]令k=j+1;再另d[k]:=a[t]。

len即为所求的解

这个方法的难点在于二分查找。注意模板的打法,正确性。向想要的靠近,记住已产生的正确的,再次二分。

code(二分法)

program liukee;
var
  a:array[1..10000] of longint;
  d:array[1..10000] of longint;
  ans,i,j,l,r,n,mid,op:longint;
begin
  readln(n);
  for i:=1 to n do
    read(a[i]);
  d[1]:=a[1];
  ans:=1;
  for i:=2 to n do
  begin
    if a[i]>=d[ans] then
    begin
      inc(ans);
      d[ans]:=a[i];
    end
    else begin
      l:=1; r:=ans;
      while l<=r do
      begin
        mid:=(l+r)>>1;
        if d[mid]<a[i] then begin op:=mid; l:=mid+1;end
                       else r:=mid-1;
      end;
      if op=0 then inc(op);
      d[op]:=a[i];
    end;
  end;
  writeln(ans);
end.

posted @ 2010-11-15 10:52  liukee  阅读(1045)  评论(0编辑  收藏  举报