/*
拆点:
将每一个节点拆为a,a1,边的权值给节点的最小消耗,如果节点a、b之间原来有边相连,则a1,b和b1,a之间连接两条边,权值为INF。
于是转换为最大流问题
新收获:我原来打算用vector存储邻接表,最后发现反向遍无法处理。终于明白了②、③处的用法,目的方便对反向遍进行运算。异或使两边对应起来
*/
//dinic实现
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int nMax = 407;
const int mMax = 200000;
const int INF = 0x3fffffff;
struct Adj
{
int v, w;
int next;
}adj[nMax + mMax];//②
int head[nMax];
int cnt;
int N, M, S, D;
int dis[nMax];
void addEdge(int u, int v, int w)
{
adj[cnt].v = v;
adj[cnt].w = w;
adj[cnt].next = head[u];
head[u] = cnt ++;
adj[cnt].v = u;
adj[cnt].w = 0;
adj[cnt].next = head[v];
head[v] = cnt ++;
}
int bfs(int s, int d)
{
queue<int> que;
que.push(s);
memset(dis, -1, sizeof(dis));
dis[s] = 0;
int i;
while(!que.empty())
{
int u = que.front();
que.pop();
for(i = head[u]; i != -1; i = adj[i].next)
{
int v = adj[i].v;
if(adj[i].w && dis[v] == -1)
{
dis[v] = dis[u] + 1;
que.push(v);
}
}
}
return dis[d];
}
int min(int a, int b)
{
return a < b ? a : b;
}
int dfs(int s, int d, int cost)
{
if(s == d) return cost;
int t, ans = 0;
int i;
for(i = head[s]; i != -1; i = adj[i].next)
{
int v = adj[i].v;
if(dis[v] == dis[s] + 1 && adj[i].w && (t = dfs(v, d, min(adj[i].w, cost))) != -1)
//①原来这里漏写了adj[i].w != 0,结果超时
{
adj[i].w -= t;
adj[i ^ 1].w += t;//③
cost -= t;
ans += t;
if(!cost) break;
}
}
return ans;
}
int dinic(int s, int d)
{
int ans = 0;
while(bfs(s, d) != -1)
{
ans += dfs(s, d, INF);
}
return ans;
}
int main()
{
//freopen("e://data.in", "r",stdin);
while(scanf("%d%d%d%d", &N, &M, &S, &D) != EOF)
{
int i;
int a, b;
memset(head, -1, sizeof(head));
cnt = 0;
for(i = 1; i <= N; ++ i)
{
scanf("%d", &a);
addEdge(i, i + N, a);
}
for(i = 1; i <= M; ++ i)
{
scanf("%d%d", &a, &b);
addEdge(a + N, b, INF);
addEdge(b + N, a, INF);
}
int ans = dinic(S, D + N);
printf("%d\n", ans);
}
return 0;
}
//isap实现
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int nMax = 407;
const int mMax = 200000;
const int INF = 0x3fffffff;
struct Adj
{
int v, w;
int next;
}adj[nMax + mMax];
int head[nMax];
int cnt;
int num[nMax];
int N, M, S, D;
int dis[nMax];
int NN;
void addEdge(int u, int v, int w)
{
adj[cnt].v = v;
adj[cnt].w = w;
adj[cnt].next = head[u];
head[u] = cnt ++;
adj[cnt].v = u;
adj[cnt].w = 0;
adj[cnt].next = head[v];
head[v] = cnt ++;
}
int min(int a, int b)
{
return a < b ? a : b;
}
int dfs(int u, int s, int d, int cost)
{
if(u == d) return cost;
int i;
int ans = 0;
int _min = NN;
for(i = head[u]; i != -1; i = adj[i].next)
{
int v = adj[i].v;
if(adj[i].w)
{
if(dis[v] + 1 == dis[u])
{
int t = dfs(v, s, d, min(adj[i].w, cost));
adj[i].w -= t;
adj[i ^ 1].w += t;
cost -= t;
ans += t;
if(dis[s] == NN) return ans;
if(!cost) break;
}
if(_min > dis[v])
_min = dis[v];
}
}
if(!ans)
{
if(-- num[dis[u]] == 0) dis[s] = NN;
dis[u] = _min + 1;
++ num[dis[u]];
}
return ans;
}
int isap(int s, int d)
{
memset(dis, 0, sizeof(dis));
memset(num, 0, sizeof(num));
num[0] = NN;
int ans = 0;
while(dis[s] < NN)
ans += dfs(s, s, d, INF);
return ans;
}
int main()
{
//freopen("e://data.in", "r",stdin);
while(scanf("%d%d%d%d", &N, &M, &S, &D) != EOF)
{
NN = 2 * N;
int i;
int a, b;
memset(head, -1, sizeof(head));
cnt = 0;
for(i = 1; i <= N; ++ i)
{
scanf("%d", &a);
addEdge(i, i + N, a);
}
for(i = 1; i <= M; ++ i)
{
scanf("%d%d", &a, &b);
addEdge(a + N, b, INF);
addEdge(b + N, a, INF);
}
int ans = isap(S, D + N);
printf("%d\n", ans);
}
return 0;
}
HDU 4289 Control(拆点,最大流)
最新推荐文章于 2020-09-01 16:17:10 发布