mayoko’s diary

プロコンとかいろいろ。

SRM 594 div1 med:FoxAndGo3

最小カットの練習がしたくなったので。

問題:TopCoder Statistics - Problem Statement

解法:最小カットを使って「燃やす埋める問題」を解くの40ページあたりから。

// max_flow
const int MAX_V = 3000;
const int INF = 1e6;
class FordFulkerson {
public:
    struct edge {
        int to;
        int cap;
        int rev;
        edge() {}
        edge(int t, int c, int r) : to(t), cap(c), rev(r) {}
    };
    vector<edge> G[MAX_V];
    void add_edge(int from, int to, int cap) {
        G[from].push_back(edge(to, cap, G[to].size()));
        G[to].push_back(edge(from, 0, G[from].size() - 1));
    }
    int max_flow(int s, int t) {
        int flow = 0;
        while (1) {
            for (int i = 0; i < MAX_V; i++) used[i] = false;
            int f = dfs(s, t, INF);
            if (f == 0) return flow;
            flow += f;
        }
    }
private:
    int dfs(int v, int t, int f) {
        if (v == t) return f;
        used[v] = true;
        for (int i = 0; i < G[v].size(); i++) {
            edge &e = G[v][i];
            if (!used[e.to] && e.cap > 0) {
                int d = dfs(e.to, t, min(e.cap, f));
                if (d > 0) {
                    e.cap -= d;
                    G[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    bool used[MAX_V];
};

const int dx[] = {1, 0, -1, 0};
const int dy[] = {0, 1, 0, -1};

class FoxAndGo3 {
public:
    int maxEmptyCells(vector <string> board) {
        FordFulkerson *ff = new FordFulkerson();
        int n = board.size();
        int s = n*n+1, t = s+1;
        int empty = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == 'x') continue;
                empty++;
                if (board[i][j] == '.') {
                    ff->add_edge(s, i*n+j, 0);
                    ff->add_edge(i*n+j, t, 1);
                }
                if (board[i][j] == 'o') {
                    ff->add_edge(s, i*n+j, 1);
                    ff->add_edge(i*n+j, t, 0);
                    for (int k = 0; k < 4; k++) {
                        int ny = i+dy[k];
                        int nx = j+dx[k];
                        if (ny < 0 || ny >= n || nx < 0 || nx >= n) continue;
                        if (board[ny][nx] == '.') {
                            ff->add_edge(i*n+j, ny*n+nx, INF);
                        }
                    }
                }
            }
        }
        return (empty - ff->max_flow(s, t));
    }
};