知识点梳理 离散化

关于这个算法,鄙人班上有很多人说这个算法太高级,用不着。但是不知道是不是写程风格的问题,本人觉得这个算法很好很强大。

这个算法,并不是单纯用来解决什么问题的,而是一种进化方法,就如同 HASH 可以把变态的数据的查找变得很基本一样,离散化,就是可以让你用一些很基本的算法去实现一些规模很大,但操作数很少的题目。换言之,有了离散化,像那些大型的数据结构就可以用一些杂七杂八的算法来实现。

-----------------------------------

离散化,其定义就是把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。其基本思想就是在众多可能的情况中“只考虑我需要用的值”。(这个,大家可以试想这样一个模型,给你一盘豆子,随机地洒在操场上,操场很大,豆子是随机分布的,这样一来,对于一个普通的算法来说,就需要对这个操场进行处理,显然是很费时间的。所以我们可以这样考虑:将操场“剪切”,将没有豆子的块切去,再将剩下的块组装成一大块,直接对组装好的进行处理)离散化可以改进一个低效的算法,甚至实现根本不可能实现的算法。要掌握这个思想,必须从大量的题目中理解此方法的特点。

他的实现方法,也很简单。

就是将每一个你所用到的数值进行排序,使这个数值有一个唯一对应的序号,此时,这个序号基本就相当于这个数值的另一个名字,也就是将这个数值进行“声明”,然后直接对这个序号进行操作,操作完了之后再将它对应回数值。

反正我对离散化的理解就是用区间的端点标号来代替直接将这个区间具象的表示,比方说,如果你要表示长度为 100 的一条线段,直接表示就是开一个 bool 数组(初值 false ),将 x~100+x 标记为 true ,而离散化就是开两个 l,r 数组,在 l 数组上取一个变量表示左端点,在 r 上取一个变量表示右端点。再比如,如果你要表示 5 50 550 5550 55550 5555550 555555550 5555555550 5555555555550 这一堆数,就可以先将它们排序,令其序号分别为 1~8 ,然后这一堆数就变成了 1 2 3 4 5 6 7 8 。

----------------------------------------------------

这里举一个例子:poj 2528。

题目大意是:给你一些线段,相互叠放,求它们覆盖的总长度。

标准解:线段树。

但是,对于像鄙人这种树形白痴,这个题就用浮水法水了。。。。

纯粹的浮水法是这样的:

var ca,cas:longint;
    n,ans:longint;
    i,j,k:longint;
    a:array[0..10010,1..2]of longint;
    v:array[0..10000010]of longint;
    f:array[0..10000010]of boolean;

procedure quicksort(l,r:longint);
var x,y,i,j:longint;
begin
 i:=l; j:=r; x:=a[(l+r)>>1,1];
 repeat
  while a[i,1]<x do inc(i);
  while x<a[j,1] do dec(j);
  if i<=j then
   begin
   a[0]:=a[i]; a[i]:=a[j]; a[j]:=a[0];
   inc(i); dec(j);
   end;
 until i>j;
 if l<j then quicksort(l,j);
 if i<r then quicksort(i,r);
end;

begin
  readln(cas);
  for ca:=1 to cas do
    begin

      fillchar(a,sizeof(a),0);
      fillchar(v,sizeof(v),0);
      readln(n);
      k:=0;
      for i:=1 to n do
        begin
          readln(a[i,1],a[i,2]);
          if a[i,2]>k then k:=a[i,2];
        end;

      for i:=1 to n do
        begin
          for j:=a[i,1] to a[i,2] do
            v[j]:=i;
        end;

      ans:=0;
      for i:=1 to n do
        for j:=a[i,1] to a[i,2] do
          if v[j]=i then begin inc(ans); break; end;

      writeln(ans);
    end;
end.

果断超时。所以,这里就要用到离散化这个很好很强大的东西。。。。。其实我也就是跳过了一些不可能情况,外加把判断线段改为了判断线段端点,仅此而已。

program ACRush;
var ans,x,y:array[0..10001]of longint;
    n:longint;
    i,j,k,ca,cas,sum:longint;

procedure cover(l,r,k,c:longint);
begin
  while ((k<n) and ((r<x[k]) or (l>y[k]))) do
    inc(k);
  if k>=n then begin
    inc(ans[c],r-l+1);
    exit;
  end;
  
  if l<x[k] then begin
    cover(l,x[k]-1,k+1,c);
    l:=x[k];
  end;
  if r>y[k] then begin
    cover(y[k]+1,r,k+1,c);
    r:=y[k];
  end;
end;

begin
  readln(cas);
  for ca:=1 to cas do
    begin
      sum:=0;
      fillchar(ans,sizeof(ans),0);
      readln(n);
      for i:=0 to n-1 do
        readln(x[i],y[i]);

      for i:=n-1 downto 0 do
        begin
          cover(x[i],y[i],i+1,i);
        end;

      for i:=0 to n-1 do
        begin
          if ans[i]>0 then inc(sum);
        end;

      writeln(sum);
    end;
end.

250 MS 水过。。。。

至于离散化的其他题目,参考 www.baidu.com 。。。。。。

 

 

 

posted @ 2011-12-29 11:57  木小漾  阅读(336)  评论(0编辑  收藏  举报