题目链接

最小割

建图: 每一个点u都增加一个虚点 $u\prime$ , u 和 $u\prime$之间连一条边,容量为1, 如果要删除点u,那么只要断u和$u\prime$之间的边就行。 如果u和v之间有联系,从 $u\prime$向v连一条边,容量为INF。

因为s和t不会被删除,所以这两个点与他们对应的虚点的容量为INF,而不是1,为了避免被割掉。最后增加一个源点和汇点,容量为INF。
最小割的结果就是题目要求的个数

为了保证输出删掉的点是字典序最小的,我们需要一个个枚举点,删除这个点的情况下再重建一次图,跑一次dinic,如果答案比之前的小,那么这个点就是我们需要的,否则要把这个点还原回去。

g++ 的顶点数要开很大,这里开了1000,c++ 500就够

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int Maxn = 1000+10;
const int INF = 0x3f3f3f3f;
const long long LINF = 1e18;

struct Edge {
    int v, cap, flow, next;
} edge[Maxn*Maxn*4];

int deep[Maxn], qu[Maxn], indx[Maxn], h[Maxn], edge_cnt, N;
int G[Maxn][Maxn];
bool node[Maxn];

void add(int u, int v, int c) {
    edge[edge_cnt].v = v;
    edge[edge_cnt].cap = c;
    edge[edge_cnt].flow = 0;
    edge[edge_cnt].next = h[u];
    h[u] = edge_cnt++;

    edge[edge_cnt].v = u;
    edge[edge_cnt].cap = 0;
    edge[edge_cnt].flow = 0;
    edge[edge_cnt].next = h[v];
    h[v] = edge_cnt++;
}

bool bfs(int s, int t) {
    int head = 0, tail = 0;
    memset(deep, 0, sizeof(deep));
    deep[s] = 1; qu[tail++] = s;
    while(head < tail) {
        int u = qu[head++];
        for(int i = h[u]; i != -2; i = edge[i].next) {
            Edge &e = edge[i];
            if(deep[e.v] == 0 && e.cap > e.flow) {
                deep[e.v] = deep[u]+1;
                qu[tail++] = e.v;
            }
        }
    }
    if(deep[t] > 0) return true;
    else return false;
}

int dfs(int cur, int t, int a) {
    if(cur == t || a == 0) return a;
    if(indx[cur] == -1) indx[cur] = h[cur];
    int flow = 0, f;
    for(int &i = indx[cur]; i != -2; i = edge[i].next) {
        Edge &e = edge[i];
        if(deep[e.v] == deep[cur]+1) {
            f = dfs(e.v, t, min(a, e.cap-e.flow));
            if(f > 0) {
                e.flow += f;
                edge[i^1].flow -= f;
                flow += f;
                a -= f;
                if(a == 0) break;
            }
        }
    }
    return flow;
}

int dinic(int s, int t) {
    int ans = 0;
    while(bfs(s, t)) {
        memset(indx, -1, sizeof(indx));
        ans += dfs(s, t, INF);
    }
    return ans;
}

void build(int s, int t) {
    for(int i = 0; i <= 3*N; ++i) h[i] = -2;
	edge_cnt = 0;
    add(0, s, INF);
    add(t, 2*N+1, INF);
    for(int i = 1; i <= N; ++i) {
        if(!node[i]) continue;
        if(i == s || i == t) add(i, N+i, INF);
        else add(i, N+i, 1);
    }
    
    for(int u = 1; u <= N; ++u) {
        for(int v = 1; v <= N; ++v) {
            if(G[u][v] == 1) {
                add(N+u, v, INF);
            }
        }
	}
}

int main(void)
{
	int s, t;
	scanf("%d%d%d", &N, &s, &t);
	for(int i = 1; i <= N; ++i) {
        for(int j = 1; j <= N; ++j) scanf("%d", &G[i][j]);
	}
	memset(node, true, sizeof(node));
	build(s, t);
	int sum = dinic(0, 2*N+1);
	if(G[s][t] == 1) printf("NO ANSWER!\n");
	else {
        printf("%d\n", sum);
        int tmp;
        if(sum != 0) {
            for(int i = 1; i <= N; ++i) {
                if(i == s || i == t) continue;
                node[i] = false;
                build(s, t);
                tmp = dinic(0, 2*N+1);
                if(tmp < sum) {
                    sum = tmp;
                    printf("%d", i);
                    if(sum <= 0) break;
                    else printf(" ");
                } else node[i] = true;
            }
            puts("");
        }
	}
	return 0;
}