[USACO5.4]奶牛的电信Telecowmunication
思路
最小割模型。但需要注意的是本题要割掉的是点而不是边,直接跑最大流板子是不行的。
为了将删点转化为删边,只需将原图中每个点拆分成入点(i)和出点(i+n),原图中所有指向点(i)的边都应指向入点(i),原图中所有从点(i)出发的边都应该从点(i+n)出发,而入点(i)与出点(i+n)内部连一条(i→i+n),边权为1的边即可。这样,删除这条内部的边,就等价于在原图上删去这个点。
由于源点和汇点是不可删的,(s→s+n)和(t→t+n)的边权都应为(INF)。
至于原图上的边只是为了体现连通性,在新图中边权一律为(INF)即可。
最后,因为跑最大流时实际跑的点有(2n)这么多,记得在板子里把用到(n)的地方都改一改。
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];
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 >> t;
cnt_e = 1;
f(i, 1, n) {
if (i == s || i == t) addn(cnt_e, head, e, i, i + n, INF);
//题面不严谨,没有明确指出c1和c2不能删去,但程序里需要体现这一点
//把c1和c2拆出来的入点和出点之间连一条边权INF的边,就意味着不能删去了
else addn(cnt_e, head, e, i, i + n, 1);
}
f(i, 1, m) {
int u, v;
cin >> u >> v;
addn(cnt_e, head, e, u+n, v, INF);
addn(cnt_e, head, e, v+n, u, INF);
}
cout<<ISAP(n*2);
//注意因为本题中拆了点,实际用到的点有n*2这么多,跑最大流的时候记得改
return 0;
}