mayoko’s diary

プロコンとかいろいろ。

SRM 650 div2 hard:TheKingsRoadsDiv2

完全二分木であることの必要十分条件が足りなくて何回もWAを出してしまった。

問題:http://community.topcoder.com/stat?c=problem_statement&pm=13667&rd=16314

解法:どの辺を取り除くかを全探索し、そのそれぞれについて完全二分木になる/ならないを判定する。
判定するためにはまずrootがどれになるかを調べなければならないが、これは比較的簡単で、完全二分木の性質として「rootは2つの辺を持ち、葉は1つの辺を持ち、ほかは3つの辺を持つ」という性質があるので、2つの辺を持つものをrootにすれば良い。その後、グラフが完全二分木になっているかを調べることになるが、完全二分木の条件は(木であること、すなわちループを持たないことは当然として)、
①頂点は0または2個の子を持つ
②0個の子を持つ場合、それは深さが最も深いところである
③2個の子を持つ場合、その2つの子は同じだけ子孫を持つ
これらすべてを満たすかどうかを、dfsで探索する。以下ソースコード

struct edge {
    int to;
    int id;
    edge(int t, int i) : to(t), id(i) {}
};

const int MAXN = 1050;
vector<edge> G[MAXN];

int size[MAXN];
bool visit[MAXN];

bool dfs(int v, int p, int out, int depth) {
    visit[v] = true;
    int cnt = 0;
    vector<int> tmp;
    for (auto e : G[v]) {
        if (e.id == out) continue;
        if (e.to == p) continue;
        if (visit[e.to]) return false;
        cnt++;
        if (!dfs(e.to, v, out, depth-1)) return false;
        tmp.push_back(size[e.to]);
    }
    if (cnt == 0) {
        size[v] = 1;
        return depth == 1;
    }
    if (cnt == 2) {
        if (tmp[0] != tmp[1]) return false;
        size[v] = tmp[0] + tmp[1] + 1;
        return true;
    }
    return false;
}

class TheKingsRoadsDiv2 {
public:
    string getAnswer(int h, vector <int> a, vector <int> b) {
        for (int i = 0; i < MAXN; i++) G[i].clear();
        int n = 1;
        for (int i = 0; i < h; i++) n *= 2;
        n -= 1; // nは点の数
        for (int i = 0; i < n; i++) {
            a[i]--; b[i]--;
            G[a[i]].push_back(edge(b[i], i));
            G[b[i]].push_back(edge(a[i], i));
        }
        for (int i = 0; i < n; i++) {
            // 連結の数を調べる
            int leaf = 0, root = -1, ng = 0;
            for (int j = 0; j < n; j++) {
                int cnt = 0;
                for (auto e : G[j]) {
                    if (e.id == i) continue;
                    cnt++;
                }
                if (cnt == 0) ng = 1;
                if (cnt == 1) leaf++;
                if (cnt == 2) {
                    if (root == -1) root = j;
                    else ng = 1;
                }
                if (cnt > 3) ng = 1;
            }
            printf("leaf is %d root is %d ng is %d\n", leaf, root, ng);
            if (ng == 1 || root == -1 || leaf != n/2+1) continue;
            for (int i = 0; i < n; i++) visit[i] = false;
            if (dfs(root, -1, i, h)) return "Correct";
        }
        return "Incorrect";
    }
};