C♯の勉強

C♯4.0 で TopCoder の過去問を解きます。

TopCoder SRM600: PalindromeMatrixDiv2

N,M は 2 以上 8 以下のため、「回文になる列の集合」と「回文になる行の集合」について全探索できる。

回文になる行と列がはっきりしていると、{ A[y][x] , A[H-1-y][x] , A[y][W-1-x] , A[H-1-y][W-1-x] } について、同じ値にしないといけないペア情報が確定する。

あとはそれらのペアの連結集合について、同じ値に変換するのに必要な最小の操作数を求めれば良い。

public class PalindromeMatrixDiv2 {
    static public int BitCount(long x) {
        int bitCount = 0;
        for (; x != 0; x = x & (x - 1)) bitCount++;
        return bitCount;
    }

    public class DisjointSet {
        private int[] group;
        public DisjointSet(int n) {
            group = new int[n];
            Clear();
        }
        public bool unionSet(int x, int y) {
            x = root(x); y = root(y);
            if (x != y) {
                if (group[y] < group[x]) return unionSet(y, x);
                group[x] += group[y];
                group[y] = x;
            }
            return x != y;
        }
        public bool isSameSet(int x, int y) { return root(x) == root(y); }
        public int root(int x) { return group[x] < 0 ? x : group[x] = root(group[x]); }
        public int size(int x) { return -group[root(x)]; }
        public void Clear() {
            for (int i = 0; i < group.Length; i++) {
                group[i] = -1;
            }
        }
    };


    public int minChange(string[] A, int rowCount, int columnCount) {
        int R = A.Length;
        int C = A[0].Length;

        int ans = int.MaxValue;
        for (int rowBit = 0; rowBit < 1 << R; rowBit++) {
            if (BitCount(rowBit) != rowCount) continue;
            for (int colBit = 0; colBit < 1 << C; colBit++) {
                if (BitCount(colBit) != columnCount) continue;

                DisjointSet ds = new DisjointSet(R * C);
                Func<int, int, int> toId = (row, col) => row * C + col;
                for (int i = 0; i < R; i++) {
                    if (((1 << i) & rowBit) != 0) {
                        for (int j = 0; j < C; j++) {
                            ds.unionSet(toId(i, j), toId(i, C - 1 - j));
                        }
                    }
                }
                for (int i = 0; i < C; i++) {
                    if (((1 << i) & colBit) != 0) {
                        for (int j = 0; j < R; j++) {
                            ds.unionSet(toId(j, i), toId(R - 1 - j, i));
                        }
                    }
                }

                Dictionary<int, List<int>> dic = new Dictionary<int, List<int>>();
                for (int i = 0; i < R * C; i++) {
                    int r = ds.root(i);
                    if (dic.ContainsKey(r) == false)
                        dic[r] = new List<int>();
                    dic[r].Add(i);
                }
                int change = 0;
                foreach (var d in dic) {
                    int zero = 0, one = 0;
                    foreach (var v in d.Value) {
                        int r = v / C;
                        int c = v % C;
                        if (A[r][c] == '1') one++;
                        else zero++;
                    }
                    change += Math.Min(zero, one);
                }
                ans = Math.Min(ans, change);
            }
        }
        return ans;
    }
}