Search on the blog

2012年2月8日水曜日

Facebook Hacker Cup 2012 Round2

1SUBMIT/1WAというひどい結果でした。自分の今の実力でTop100に入れるなんて思ってないし、もっと練習しなければと思いました。とりあえず復習してAとBは解けたので簡単な解説を書きます。

Road Removal
先に重要じゃない都市同士を結ぶ道路をすべて採用します。するとグラフが、重要じゃない都市のみで構成される複数の連結成分と複数の重要な都市から成り立つ森になります。前者の連結成分を一つの節点とみなしてクラスカル法のようなことをすればOK。使用するアルゴリズムや実装は簡単だけどどのアルゴリズムを適用すれば良いのかが見えにくいという問題で、個人的には良問だと思います。
const int MAX = 10000;
int par[10000];
int rank[10000];

void init(int n) {
    REP(i, n) {
        par[i] = i;
        rank[i] = 0;
    }
}

int find(int x) {
    if (x == par[x])
        return x;
    return par[x] = find(par[x]);
}

bool same(int x, int y) {
    return find(x) == find(y);
}

void merge(int x, int y) {
    x = find(x);
    y = find(y);

    if (x == y)
        return;
    if (rank[x] > rank[y]) {
        par[y] = x;
    } else {
        par[x] = y;
        if (rank[x] == rank[y])
            rank[y]++;
    }
}

void solve() {
    int T;

    scanf("%d", &T);
    REP(t, T) {
        int N, M, K;

        scanf("%d %d %d", &N, &M, &K);
        init(N);

        int a, b;
        vector<pair<int, int> > road;
        REP(i, M) {
            scanf("%d %d", &a, &b);
            if (min(a, b) >= K)
                merge(a, b);
            else
                road.push_back(MP(a, b));
        }

        int ret = 0;
        REP(i, road.size()) {
            a = road[i].first;
            b = road[i].second;

            if (same(a, b))
                ++ret;
            else
                merge(a, b);
        }

        printf("Case #%d: %d\n", t+1, ret);
    }
}



Monopoly
Union Findと動的計画の合わせ技です。dp[i][j]に会社iの社長が直属の部下をj人持つときの木の高さの最小値を保持します。あとポイントとなるのは、dp[i][j]の更新を高速に計算するためにminHeight[i]に現時点における会社iが取り得る木の高さの最小値を保存しておくことです。Union Findの一度の操作をO(log(N))とすると、一つの問題はO(N log(N) + ND)で計算できます。

const int INF = 999999999;
const int MAX_N = 30000;
const int MAX_D = 5000;
int dp[MAX_N][MAX_D+1];
int minHeight[MAX_N];
int par[MAX_N];
int rank[MAX_N];

void init(int n) {
    REP(i, n) {
        par[i] = i;
        rank[i] = 0;
    }
}

int find(int x) {
    if (x == par[x])
        return x;
    return par[x] = find(par[x]);
}

void merge(int x, int y) {
    x = find(x);
    y = find(y);

    if (x == y) return;
    if (rank[x] > rank[y])
        par[y] = x;
    else {
        par[x] = y;
        rank[y]++;
    }
}

bool same(int x, int y) {
    return find(x) == find(y);
}

void solve() {
    int T;
    scanf("%d", &T);

    REP(t, T) {
        int N, D;
        scanf("%d %d", &N, &D);

        init(N);
        REP(i, N) REP(j, D+1) dp[i][j] = INF;
        REP(i, N) {
            minHeight[i] = 1;
            dp[i][0] = 1;
        }

        int a, b;
        REP(i, N-1) {
            scanf("%d %d", &a, &b);
            a = find(a-1);
            b = find(b-1);

            merge(a, b);
            int x = find(a);
            dp[x][0] = INF;
            for (int j = D; j > 0; j--)
                dp[x][j] = min(max(dp[a][j-1], 1+minHeight[b]), // a becomes the president
                             max(dp[b][j-1], 1+minHeight[a])); // b becomes the president

            minHeight[x] = INF;
            REP(j, D+1)
                minHeight[x] = min(minHeight[x], dp[x][j]);
        }

        int ret = INF;
        int x = find(0);
        REP(i, D+1)
            ret = min(ret, dp[x][i]);

        printf("Case #%d: %d\n", t+1, ret);
    }
}



0 件のコメント:

コメントを投稿