Problem : A | B | C | D | E | F | G | H

H - Pasangan Paling "Wah"

(pembahasan oleh Felix Halim)

Ada setidaknya 2 cara untuk menyelesaikan soal wah:

1. Brute Force O(N^2 * log(N))

Basically melakukan apa yang diminta oleh soal. Generate N x N co-ce pairs dan calculate wah value untuk setiap pair. Lalu sort N^2 pair ini dan iterate dari yang wah value paling besar: Untuk setiap pasangan co-ce yang masih single dua duanya, maka pasangkan (output). Cara ini tentu saja akan mendapat Time Limit Exceeded.

2. Sort O(N log N) + Greedy O(N)

Sort semua value dari co beserta index nya ascending, begitu pula untuk ce. Untuk mencetak pasangan paling wah, kita hanya perlu mengecek 2 tipe pasangan:
- co dengan value kecil dipasangkan dengan ce value besar.
- co dengan value besar dipasangkan dengan ce value kecil.
Jika ada co/ce dengan value sama, ambil yang index terkecil (jangan lupa ini!)
Dari dua kemungkinan pasangan tersebut, kita tinggal print yang pasangan wah nya terbesar. Algoritma pemasangan greedy ini jalan dalam O(N).

Implementasi pemasangan greedy ini sedikit merepotkan dalam mencari co/ce dengan value terbesar tapi memiliki index terkecil. Bagi mereka yang sudah menguasai STL, maka implementasinya akan gampang dilakukan dengan <set> of pair<int,int>. Bagi mereka yang belum menguasai STL, maka implementasinya akan sedikit lebih susah menggunakan index, dan mempunyai resiko bug yang tinggi.

Solusi C/C++   (oleh Felix Halim)
#include <stdio.h>
#include <stdlib.h>

int back(int X[][2], int i, int n){
  while (i>0 && X[i-1][0] != -1 && X[i-1][0] == X[i][0]) i--;
  if (X[i][0] != -1) return i;
  if (i+1<n && X[i+1][0] != -1) return i+1;
  while (i>0 && (X[i][0] == -1 || X[i-1][0] == X[i][0])) i--;
  return i;
}

int X[10000][2], Y[10000][2];

int cmp(const void *a, const void *b){
  int *x = (int*) a, *y = (int*) b;
  if (x[0] != y[0]) return x[0] - y[0];
  return x[1] - y[1];
}

int main(){
  for (int n; scanf("%d",&n)!=EOF; ){
    for (int i=0; i<n; i++) scanf("%d",&X[i][0]), X[i][1] = i;
    for (int i=0; i<n; i++) scanf("%d",&Y[i][0]), Y[i][1] = i;

    qsort(X, n, sizeof(X[0]), cmp);
    qsort(Y, n, sizeof(Y[0]), cmp);
    
    for (int i=0, x1=0, x2=n-1, y1=0, y2=n-1; i<n; i++){
      while (x1<n && X[x1][0]==-1) x1++;
      while (y1<n && Y[y1][0]==-1) y1++;
      x2 = back(X, x2, n);
      y2 = back(Y, y2, n);

      int *co1 = X[x1], *co2 = X[x2], *ce1 = Y[y1], *ce2 = Y[y2];
      int co1ce2 = (co1[0] - ce2[0]) * (co1[0] - ce2[0]);
      int co2ce1 = (co2[0] - ce1[0]) * (co2[0] - ce1[0]);

      int cmp = co2ce1 - co1ce2;      // wah value terbesar
      if (cmp==0) cmp = co1[1] - co2[1];  // index co terkecil
      if (cmp==0) cmp = ce2[1] - ce1[1];  // index ce terkecil

      if (cmp < 0){
        co1[0] = ce2[0] = -1;
        printf("%d %d %d\n",co1[1],ce2[1],co1ce2);
      } else {
        co2[0] = ce1[0] = -1;
        printf("%d %d %d\n",co2[1],ce1[1],co2ce1);
      }
    }
    puts("");
  }
}
Solusi Pascal   (oleh Eko Wibowo)
type
  node = record
  value, idx : longint;
  end;
  tlist = array[1..10000] of node;

var
  t, n, i, x1, x2, y1, y2, co2ce1, co1ce2, cmp : longint;
  x, y : tlist;

function isSmaller(a,b : node):boolean;
begin
  if (a.value <> b.value) then isSmaller := a.value < b.value
  else isSmaller := a.idx < b.idx;
end;

// diambil dari contoh pada free-pascal
procedure sort(var a : tlist; l, r : longint);
var i, j : longint;
    x, y : node;
begin
  i:=l;
  j:=r;
  x:=a[(l + r) div 2];
  repeat
    while isSmaller(a[i], x) do i := i + 1;
    while isSmaller(x, a[j]) do j := j - 1;
    if i <= j then
    begin
      y := a[i];
      a[i] := a[j];
      a[j] := y;
      i := i + 1;
      j := j - 1;
    end;
  until i > j;
  if l < j then sort(a,l,j);
  if i < r then sort(a,i,r);
end;

function back(v : tlist; i, n : longint) : longint;
begin
  while (i > 1) and (v[i-1].value <> -1) and (v[i-1].value = v[i].value) do i := i-1;
  if (v[i].value <> -1) then back := i
  else if (i + 1 <= n) and (v[i + 1].value <> -1) then back := i + 1
  else
  begin
    while (i > 1) and ((v[i].value = -1) or (v[i-1].value = v[i].value)) do
      i := i - 1;
    back := i;
  end;
end;

begin
  read(t);
  while (t > 0) do
  begin
    t := t - 1;
    read(n);
    for i := 1 to n do
    begin
      read(x[i].value);
      x[i].idx := i;
    end;
    for i := 1 to n do
    begin
      read(y[i].value);
      y[i].idx := i;
    end;
    sort(x,1,n);
    sort(y,1,n);
    x1 := 1;
    x2 := n;
    y1 := 1;
    y2 := n;
    for i := 1 to n do
    begin
      while (x1 <= n) and (x[x1].value = -1) do x1 := x1 + 1;
      while (y1 <= n) and (y[y1].value = -1) do y1 := y1 + 1;
      x2 := back(x,x2,n);
      y2 := back(y,y2,n);
      co1ce2 := sqr(x[x1].value - y[y2].value);
      co2ce1 := sqr(x[x2].value - y[y1].value);

      cmp := co2ce1 - co1ce2;
      if (cmp=0) then cmp := x[x1].idx - x[x2].idx;
      if (cmp=0) then cmp := y[y2].idx - y[y1].idx;

      if (cmp < 0) then
      begin
        x[x1].value := -1;
        y[y2].value := -1;
        writeln(x[x1].idx-1,' ',y[y2].idx-1,' ',co1ce2);
      end
      else
      begin
        x[x2].value := -1;
        y[y1].value := -1;
        writeln(x[x2].idx-1,' ',y[y1].idx-1,' ',co2ce1);
      end;
    end;
    writeln();
  end;
end.