AOJ : 0518 - 最古の遺跡 (The Oldest Site)

問題概要

http://rose.u-aizu.ac.jp/onlinejudge/ProblemSet/description.jsp?id=0518&lang=jp
問題文が日本語なので省略です

アルゴリズム

単純に四重ループで四角形の4点を決めて、正方形かどうか判断する方法では、O(n^4)となって、TLEになることが予想できます。
そのため、次のようにO(n^2)のアルゴリズムにすれば、通せます。

  1. 二重ループによって、点の集合からある2点A,Bを選択する。
  2. 線分ABの単位法線ベクトル V を出す。
  3. 次の2つの条件が成り立つなら4へ、そうでないなら1へ戻る。
    • 点Aから単位法線ベクトルVを線分ABの距離の長さ分伸ばした位置に点がある(これを点Cとする)
    • 点Bから単位法線ベクトルVを線分ABの距離の長さ分伸ばした位置に点がある(これを点Dとする)
  4. 正方形ABDCの面積を求め、最大面積より大きいなら更新する。

ただし、点CやDの点があるかどうか検索するとき、セットなどで管理している場合、検索に時間がかかってしまいます。
そのため、O(1)でヒットするように、2次元配列で管理します。
この際、int型にするとMLEになるため、char型にする必要があります。

プログラム

#include <iostream>
#include <complex>
#include <vector>
#include <cstring>
using namespace std;

#define REP(i,n,m) for(int i=n;i<m;i++)
#define rep(i,n) REP(i,0,n)

typedef complex<double> P;

P p[3001];
char flg[5001][5001]; //[x][y]が 1 : (x,y)の点がある, 0 : (x,y)の点がない

//外積
double cross(P a,P b){
  return (a.real() * b.imag() - a.imag() * b.real());
}

//面積
double area(vector<P> v){
  int n = v.size();
  double res = 0.0;
  rep(i,n) res += cross(v[i],v[(i+1)%n]);
  return fabs(res/2);
}

int main(void){
  int n;

  while(cin>>n,n){
    memset(flg,0,sizeof(flg));

    rep(i,n){
      int x,y;
      cin>>x>>y;
      p[i] = P(x,y);
      flg[x][y] = 1;
    }

    double ans = 0.0;

    //2点どれかを選ぶ
    rep(i,n) REP(j,i+1,n) {
      P ab = p[i] - p[j];
      P un = (ab * P(0, +1)) / abs(ab); //abの単位法線ベクトル
      P c = p[i] + un * abs(ab);
      P d = p[j] + un * abs(ab);

      if(c.real() - (int)c.real() != 0.0 ||
         c.imag() - (int)c.imag() != 0.0 ||
         d.real() - (int)d.real() != 0.0 ||
         d.imag() - (int)d.imag() != 0.0 ||
         c.real() < 0 || c.imag() < 0 ||
         d.real() < 0 || d.imag() < 0 ||
         c.real() > 5000 || c.imag() > 5000 ||
         d.real() > 5000 || d.imag() > 5000) continue;

      //点c,dが存在するなら正方形がある
      if(flg[(int)c.real()][(int)c.imag()] && flg[(int)d.real()][(int)d.imag()]){
        vector<P> v;
        v.push_back(p[i]);
        v.push_back(p[j]);
        v.push_back(d);
        v.push_back(c);
        double cal = area(v);
        if(ans < cal) ans = cal;
      }
    }

    cout<<(int)ans<<endl;
  }

  return 0;
}