【最小割】[SHOI2007]善意的投票

[SHOI2007]善意的投票

思路

把小朋友视为点,“不睡午觉”和“睡午觉”这两种结果作为源点和汇点。

要求冲突数最小,即找出一种割边方式使点划分为两个集合,且割掉的边数最小。

将想“不睡午觉”的小朋友与源点连一条边\(s→i\),如果割掉了这条边表示这个小朋友与自身意愿冲突;同理,将想“睡午觉”的小朋友与汇点连一条边\(i→t\)

最后,好朋友之间连双向边,割掉边代表违背了好朋友的意愿。

以上所有边权均为\(1\)

然后跑最大流即可。注意因为加上了源点和汇点,实际图上的点有\(n+2\)这么多

void addn(int&cnt_e, int head[], Edge e[], int u, int v, LL w) {
    //网络流建图
    e[++cnt_e].next = head[u]; e[cnt_e].from = u; e[cnt_e].to = v; e[cnt_e].w = w; head[u] = cnt_e;
    e[++cnt_e].next = head[v]; e[cnt_e].from = v; e[cnt_e].to = u; e[cnt_e].w = 0; head[v] = cnt_e;
}

int cnt_e = 0, head[maxn], n, m;
int s, t;
int cur[maxn], depth[maxn], gap[maxn];
int wish[maxn];
LL Maxflow = 0;

void bfs() {
    mem(depth, -1);
    mem(gap, 0);
    depth[t] = 0;
    gap[0] = 1;
    cur[t] = head[t];
    queue<int> q;
    q.push(t);
    while (q.size()) {
        int u = q.front(); q.pop();
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].to;
            if (depth[v] != -1) continue;
            q.push(v);
            depth[v] = depth[u] + 1;
            gap[depth[v]]++;
        }
    }
    return;
}

LL dfs(int now, LL minflow,int n) {
    if (now == t) {
        Maxflow += minflow;
        return minflow;
    }
    LL nowflow = 0;
    for (int i = cur[now]; i; i = e[i].next) {
        cur[now] = i;
        int v = e[i].to;
        if (e[i].w && depth[v] + 1 == depth[now]) {
            LL k = dfs(v, min(e[i].w, minflow - nowflow), n);
            if (k) {
                e[i].w -= k;
                e[i ^ 1].w += k;
                nowflow += k;
            }
            if (minflow == nowflow) return nowflow;
        }
    }
    gap[depth[now]]--;
    if (!gap[depth[now]]) depth[s] = n + 1;
    depth[now]++;
    gap[depth[now]]++;
    return nowflow;
}

LL ISAP(int n) {
    Maxflow = 0;
    bfs();
    while (depth[s] < n) {
        memcpy(cur, head, sizeof(head));
        dfs(s, INF, n);
    }
    return Maxflow;
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m;
    s = n + 1; t = n + 2;
    cnt_e = 1;
    f(i, 1, n) {
        cin >> wish[i];
        if (!wish[i]) addn(cnt_e, head, e, s, i, 1);
        else addn(cnt_e, head, e, i, t, 1);
    }
    f(i, 1, m) {
        int u, v; cin >> u >> v;
        addn(cnt_e, head, e, u, v, 1);
        addn(cnt_e, head, e, v, u, 1);
    }
    cout << ISAP(n+2);
    return 0;
}
posted @ 2020-10-30 17:38  StreamAzure  阅读(74)  评论(0编辑  收藏  举报