网络流 拆点+最小割点集
作者:互联网
P4662 [BalticOI 2008]黑手党
题目描述
Byteland 国警方收到了一条匿名举报,其中说当地黑帮老大正计划一次从港口到郊区仓库的运输。警方知道运输的时间并且知道运输需要用到国家的高速公路网。
高速公路网包含双向的高速公路段,每个路段直接连着两个不同的收费站。一个收费站可能与很多其他的收费站相连。汽车只能通过收费站进入或离开高速公路网。据所知,黑帮会距港口边最近的收费站进入高速公路,从距仓库最近的收费站离开(不会在出高速后重新进入)。特警组位于选定的收费站处。当运输车辆进入被监控的收费站时,它就会被警察抓住。
从这个角度看,最简单的办法就是在每个收费站处都安排特警班。然而,控制一个收费站需要特定的费用,每个收费站费用不同。警方想要让花费最小,所以他们需要制定一个收费站的最小控制集,这个集合满足两个条件:
所有从港口到仓库的交通必须至少经过集合中的一个收费站
监控这些收费站的费用(即监控每一个收费站费用之和)最小
你可以假设使用高速公路可以从港口到仓库。
任务
写一个程序能够:
从标准输入中读取高速公路网,监控代价和运输的起点和终点
找到收费站的最小控制集
输出这个集合到标准输出
输入格式
标准输入的第一行包含两个整数 n 和 m,表示收费站的总数和公路段的总数。收费站按 1 到 n 标号;
第二行包含两个整数 a 和 b,分别表示距港口和仓库最近的两个收费站编号;
接下来 n 行表示控制费用,第 i 行包含一个整数,表示第 i 个收费站的控制费用 c;
接下来 m 行表示高速公路网,第 j 行包含两个整数 x 和 y,表示在 x 和 y 收费站之间有一条公路段相连。每一条高速公路段只出现一次。
输出格式
唯一的一行输出应包含最小控制集中收费站的编号,以递增顺序输出,用一个空格分隔。
如果有多于一个最小控制集,你的程序可以输出任意一个。
输入
5 6
5 3
2
4
8
3
10
1 5
1 2
2 4
4 5
2 3
3 4
输出
1 4
思路
拆点之后。把费用当流量,就是最小割。
我们在残余网络DFS。不经过满流的边,那么如果一个点拆的2个点一个在S集合。一个在T集合。那么这个点就是选择的。因为源汇可以拆
我们应该建立虚的源汇。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
const int maxm= 1e6+ 10;
const int INF = 0x3f3f3f3f;
//注释为弧优化
struct max_Folw {
int d[maxn], cur[maxn], start, tend;
int Q[maxn * 2];
struct node {
int to, cap, flow, next;
} edge[maxm << 1];
int head[maxn];
bool vis[maxn];
int cnt;
void init(int s, int t){
memset(head, -1, sizeof(head));
cnt=0;
start=s, tend=t;
}
void add(int start, int to, int cap) {
edge[cnt] = {to, cap, 0, head[start]};
head[start] = cnt++;
}
void AddEdge(int start, int to, int cap){
add(start, to, cap);
add(to, start, 0);
}
bool BFS() {
memset(d, -1, sizeof(d));
int Thead, Ttail;
Thead = Ttail = 0;
Q[Ttail++] = tend;
d[tend] = 0;
while (Thead<Ttail) {
int x = Q[Thead];
if (x == start)
return true;
for (int i = head[x]; i != -1; i = edge[i].next) {
int temp = edge[i].to;
if (d[temp] == -1 && edge[i^1].cap > edge[i^1].flow) { //没有标记,且可行流大于0
d[temp] = d[x] + 1;
Q[Ttail++] = temp;
}
}
Thead++;
}
return false;//汇点是否成功标号,也就是说是否找到增广路
}
int DFS(int x, int cap) {
if (x == tend)
return cap;
int flow = 0, f;
//for (int i = cur[x]; i != -1; i = edge[cur[x]=i].next) {
for (int i = head[x]; i != -1; i = edge[i].next) {
int temp = edge[i].to;
if (d[temp] == d[x] - 1 && edge[i].cap > edge[i].flow) {
f = DFS(temp, min(cap - flow, edge[i].cap - edge[i].flow));
edge[i].flow += f;
edge[i ^ 1].flow -= f;
flow += f;
if (flow == cap)
return flow;
}
}
d[x] = -2;//防止重搜
return flow;
}
int maxflow() {
int flow = 0, f;
while (BFS()) {
//memcpy(cur, head, sizeof head);
flow += DFS(start, INF);
}
return flow;
}
//得到割点集合
queue<int> q;
void bfs(){
memset(vis, 0, sizeof(vis));
vis[start]=1;
q.push(start);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i = head[u]; i != -1; i = edge[i].next){
int to=edge[i].to, w=edge[i].cap-edge[i].flow;
if(w&&!vis[to]){
vis[to]=1;
q.push(to);
}
}
}
}
}flow;
//edge[i].cap-edge[i].flow==0 容量-流量=可流的流量 =0说明已经满流
int a[maxn];
set<int> st;
int main() {
int n, m, A, B; scanf("%d%d%d%d",&n, &m, &A, &B);
flow.init(0, 2*n+5);
flow.AddEdge(0, A, INF);
flow.AddEdge(B+n, 2*n+5, INF);
for(int i=1; i<=n; i++){
scanf("%d", &a[i]);
flow.AddEdge(i, i+n, a[i]);
}
for(int i=1; i<=m; i++){
int x, y; scanf("%d%d", &x, &y);
flow.AddEdge(x+n, y, INF);
flow.AddEdge(y+n, x, INF);
}
flow.maxflow();
flow.bfs();
for(int i=1; i<=n; i++){
if((flow.vis[i]&&!flow.vis[i+n])||(!flow.vis[i]&&flow.vis[i+n])){
st.insert(i);
}
}
for(auto x: st){
printf("%d ", x);
}
printf("\n");
return 0;
}
标签:cap,割点集,拆点,收费站,flow,最小,int,edge,maxn 来源: https://www.cnblogs.com/liweihang/p/13970049.html