Search on the blog

2012年4月7日土曜日

SRM 283 DIV2 600 PowerSupply

問題
 デカルト平面上にN個の点(xi, yi)がある。そこにx-軸、y-軸、2つの対角軸のいずれかに平行な線を一本引く。この線からの距離がD以下となるような点の個数を最大化したい。最適な線を引いた場合にえられるこの様な点の個数の最大値を求めよ。
 0 ≦ N ≦ 50
 -1000000 ≦ x≦ 1000000
 -1000000 ≦ y≦ 1000000

方針

 まず、x-軸に平行な線を引く場合を考える。点を2つ選んでその2つの距離が2*D以下なら、その2つの点および2つの点の間にある点すべてを距離D以内に含むような線が引ける。これは幾何の問題で良く使う”ギリギリ”を考える手法。
 y-軸も同様に可能。対角軸に平行な線を考えるのはちょっと面倒。点(x1, y1)を通りx+y=0に平行な直線(L1と定義する)と点(x2, y2)を通りx+y=0に平行な直線(L2の定義する)の距離を計算する場合は、L1とx+y=0の距離をまず測り、次にL2とx+y=0の距離を測って、その差分を取ればよいことが分かる。

 あとは、平方根が出てくるので丸め誤差に注意。double使いたくない場合は、長さの2乗を考えればOK。


ソースコード
 md[]とかcd[]とか使ってるけど、これはmain diagonalとcollateral diagonalの略。それぞれ行列の対角成分を呼ぶ呼び方らしい(今日別の問題解いてるときに知った)。main diagonalは右下がりのいわゆる主対角成分。collateral diagonalは右上がりの対角成分。
#define REP(i, n) for(int i=0; i<(int)(n); i++)
#define FOR(i, s, e) for (int i = (int)(s); i < (int)(e); i++)

class PowerSupply {
    int maxLen(vector<int> x, long long len) {
        int ret = 0;

        sort(x.begin(), x.end());
        REP(i, x.size()) {
            FOR (j, i, x.size()) {
                long long diff = x[j] - x[i];
                diff *= diff;
                if (diff <= len)
                    ret = max(ret, j-i+1);
            }
        }

        return ret;
    }

public:
    int maxProfit(vector<int> x, vector<int> y, int D) {
        int ret = 0;
        int N = x.size();

        vector<int> md = x;
        vector<int> cd = x;

        REP(i, N) md[i] = x[i] + y[i];
        REP(i, N) cd[i] = x[i] - y[i];

        long long D1 = 4LL * D * D;           // orthogonal
        long long D2 = 8LL * D * D;              // diagonal

        ret = max(ret, maxLen(x, D1));
        ret = max(ret, maxLen(y, D1));
        ret = max(ret, maxLen(md, D2));
        ret = max(ret, maxLen(cd, D2));

        return ret;
    }
};

0 件のコメント:

コメントを投稿