士兵列队(warrior)

sqybi 发表于 2007-10-28 11:55:53

有N个士兵分散在一个网格型的土地上。网格土地上每一个格子位置由一对整数坐标给出,士兵可以移动,每次移动可以让某个士兵向上、向下、向左或向右移动一个单元格。
这些士兵最后要进入某一行且排在一起(即他们最终的位置为(x,y),(x+1,y),…,(x+N-1,y),点(x,y)是任意的)。请问最少需要多少次移动才能做到?假设格子无限大,可以同时容纳无限多个士兵。

输入(warrior.in)
第一行一个整数N
下接n行每行两个整数描述一个士兵的初始坐标。数据保证没有两个士兵,他们的横坐标或者纵坐标相同。

输出(warrior.out)
最少移动次数

数据范围
30%的数据N<=10000
100%的数据N<=10^6,坐标范围[-10^8,10^8]

算法解析
实际上是一道数学题.
这道题可以分两步来做:第一步是将所有人放到一行上,第二步是将他们排到连续的一条链上.
第一步其实很简单,我们可以发现,只要放到中间那个人所在的一行上(如果n是偶数,那么只要放到中间的两个人之间任意的一行上就可以),就可以保证移动的步数最小.
第二步实际上和第一步很相似,只需要让所有人按照原来的顺序排列就可以了,只是每两个人之间的距离都变成了1.让原始状态下中间那个人成为最终这条链的中间那个人,可以保证移动次数最少,证明略过.
可以做两次qsort,实际应用上发现常数大的惊人,不过用我这个写得很ws的程序还是AC了,有好几个0.9s以上的点...好险...

程序
//for my winsty
{$APPTYPE CONSOLE}
program warrior_sqybi;
  const
    fin = 'warrior.in';
    fout = 'warrior.out';
    nn = 1000000;

  type
    arr = array[1..nn]of longint;

  var
    n, i, t, p, mid: longint;
    x, y: arr;
    s: int64;

  procedure qsort(var a: arr; l, r: longint);
    var
      i, j, d, t: longint;
    begin
      i := l;
      j := r;
      d := a[(l+r) div 2];
      repeat
        while a[i] < d do inc(i);
        while a[j] > d do dec(j);
        if i <= j then begin
          t := a[i];
          a[i] := a[j];
          a[j] := t;
          inc(i);
          dec(j);
        end;
      until i > j;
      if l < j then qsort(a, l, j);
      if i < r then qsort(a, i, r);
    end;

  begin
    assign(input, fin);
    assign(output, fout);
    reset(input);
    rewrite(output);
    readln(n);
    for i:=1 to n do
      readln(x[i], y[i]);
    qsort(y, 1, n);
    mid := y[(n+1) div 2];
    s := 0;
    for i:=1 to n do
      s := s + abs(y[i]-mid);
    qsort(x, 1, n);
    mid := x[(n+1) div 2];
    t := (n+1) div 2;
    for i:=1 to n div 2 do begin
      p := mid - (t - i);
      s := s + abs(p-x[i]);
    end;
    for i:=n div 2 + 1 to n do begin
      p := mid + (i - t);
      s := s + abs(p-x[i]);
    end;
    writeln(s);
    close(input);
    close(output);
  end.

关键词(Tag): 数学


最新评论

发表评论

*昵称

已经注册过? 请登录

Email
网址
*评论