diff --git a/省选准备/assets/day6/poj1273_ISAP.cpp b/省选准备/assets/day6/poj1273_ISAP.cpp new file mode 100644 index 0000000..ce5db76 --- /dev/null +++ b/省选准备/assets/day6/poj1273_ISAP.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +inline int min(int a, int b) { return a < b ? a : b; } +inline void read(int &x) +{ + int ch = x = 0; + while (!isdigit(ch = getchar())); + for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0'; +} +const int N = 205, inf = 0x3f3f3f3f; +int adj[N], nxt[N << 1], to[N << 1], cap[N << 1], cur[N], cnt[N], dis[N], fa[N], ecnt; +inline void addEdge_impl_(int f, int t, int c) +{ + nxt[ecnt] = adj[f]; + adj[f] = ecnt; + to[ecnt] = t; + cap[ecnt] = c; + ecnt++; +} +inline void addEdge(int f, int t, int c) +{ + addEdge_impl_(f, t, c); + addEdge_impl_(t, f, 0); +} +int ISAP(int S, int T) +{ + int flow = 0; + for (int i = 0; i < N; i++) dis[i] = N - 1; + int len = 0, x; + static int que[N]; + dis[que[len++] = T] = 0; + for (int i = 0; i < len; i++) + for (int e = adj[x = que[i]]; ~e; e = nxt[e]) + if (cap[e ^ 1] && dis[to[e]] > dis[x] + 1) + dis[que[len++] = to[e]] = dis[x] + 1; + memset(cnt, 0, sizeof(cnt)); + for (int i = 0; i < N; i++) cur[i] = adj[i], cnt[dis[i]]++; + x = S; + while (dis[S] < N - 1) + { + if (x == T) + { + int curFlow = inf; + for (x = T; x != S; x = to[fa[x] ^ 1]) curFlow = min(curFlow, cap[fa[x]]); + for (x = T; x != S; x = to[fa[x] ^ 1]) cap[fa[x]] -= curFlow, cap[fa[x] ^ 1] += curFlow; + flow += curFlow, x = S; + } + bool needRetreat = true; + for (int e = cur[x]; needRetreat && ~e; e = nxt[e]) + if (cap[e] && dis[x] == dis[to[e]] + 1) + needRetreat = false, cur[x] = e, fa[x = to[e]] = e; + if (needRetreat) + { + int nd = N - 2; + for (int e = adj[x]; ~e; e = nxt[e]) + if (cap[e]) nd = min(nd, dis[to[e]]); + if (--cnt[dis[x]] == 0) break; + ++cnt[dis[x] = nd + 1]; + cur[x] = adj[x]; + if (x != S) x = to[fa[x] ^ 1]; + } + } + return flow; +} +int main() +{ + int n, m; + while (~scanf("%d%d", &m, &n)) + { + memset(adj, -1, sizeof(adj)), ecnt = 0; + for (int i = 0, x, y, z; i < m; i++) + scanf("%d%d%d", &x, &y, &z), addEdge(x, y, z); + printf("%d\n", ISAP(1, n)); + } +} \ No newline at end of file diff --git a/省选准备/assets/day6/poj1273_dinic.cpp b/省选准备/assets/day6/poj1273_dinic.cpp new file mode 100644 index 0000000..39ae7b5 --- /dev/null +++ b/省选准备/assets/day6/poj1273_dinic.cpp @@ -0,0 +1,60 @@ +#include +#include +inline int min(int a, int b) { return a < b ? a : b; } +const int inf = 0x3f3f3f3f; +int adj[205], nxt[405], to[405], cap[405], cur[205], dis[405], ecnt; +inline void addEdge_impl_(int f, int t, int c) +{ + nxt[ecnt] = adj[f]; + adj[f] = ecnt; + to[ecnt] = t; + cap[ecnt] = c; + ecnt++; +} +inline void addEdge(int f, int t, int c) +{ + addEdge_impl_(f, t, c); + addEdge_impl_(t, f, 0); +} +int n, m; +bool bfs() +{ + static int que[205]; + for (int i = 1; i <= n; i++) + cur[i] = adj[i], dis[i] = inf; + int len = dis[1] = 0; + que[len++] = 1; + for (int i = 0; i < len; i++) + for (int e = adj[que[i]]; ~e; e = nxt[e]) + if (cap[e] && dis[to[e]] > dis[que[i]] + 1) + dis[que[len++] = to[e]] = dis[que[i]] + 1; + return dis[n] < inf; +} +int dinic(const int u, const int flow) +{ + if (u == n) + return flow; + int res = 0; + for (int e = cur[u], curFlow; ~e; e = nxt[e]) + if (cap[e] && dis[u] == dis[to[e]] - 1) + if (curFlow = dinic(to[e], min(cap[e], flow - res))) + res += curFlow, cap[e] -= curFlow, cap[e ^ 1] += curFlow, cur[u] = e; + if (res != flow) + dis[u] = inf; + return res; +} +int main() +{ + while (~scanf("%d%d", &m, &n)) + { + ecnt = 0; + memset(adj, -1, sizeof(adj)); + for (int i = 0, x, y, z; i < m; i++) + scanf("%d%d%d", &x, &y, &z), addEdge(x, y, z); + int ans = 0; + while (bfs()) + ans += dinic(1, inf); + printf("%d\n", ans); + } + return 0; +} \ No newline at end of file diff --git a/省选准备/省选基础算法.pdf b/省选准备/省选基础算法.pdf index 8fca381..6cabaf2 100644 Binary files a/省选准备/省选基础算法.pdf and b/省选准备/省选基础算法.pdf differ diff --git a/省选准备/省选基础算法.tex b/省选准备/省选基础算法.tex index 0f7f800..7ee2854 100644 --- a/省选准备/省选基础算法.tex +++ b/省选准备/省选基础算法.tex @@ -481,4 +481,12 @@ Manacher 模板题 \paragraph{练习题} \subparagraph{\href{http://poj.org/problem?id=2187}{POJ2187} - Beauty Contest} 模板题,注意符号要写对 \codeinput[Beauty Contest]{assets/day5/POJ2187.cpp} +\section{day6 网络流} +\subsection{最大流/最小割} 我不想抄讲义了,就通俗易懂的说一下。每次找一条从源点到汇点的可行流,增广,直到源点和汇点不联通。就这样。Dinic和ISAP都是最短增广路算法,不同的就是维护增广路的方式。 +\paragraph{Dinic} 虽然我并不使用Dinic,不过,我认为研究这种算法也是有其必要性的。比如可以加深对费用流的理解(滑稽)。 +\codeinput[\href{http://poj.org/problem?id=1273}{POJ1273} - Drainage Ditches (Dinic)]{assets/day6/poj1273_dinic.cpp} +对这段代码简单的说两句。值得注意的有这么几点,一是\verb|cur[u]|的使用,在单次增广时可以避免无谓的探查;二是40,41行在没有把以上配额用光之前没有返回可以造成多路增广的情况来增加效率;三是第42,43行的判断,如果所有子节点的可用流之和都不能满足当前点的配额,这个点就没有访问的必要了。我想大概就这么些。 +\paragraph{ISAP} 我经常使用ISAP,因为据说这玩意更稳定,由于我的脸黑,我怕碰上一些恶心的数据卡掉Dinic,并且目前没有一道普通网络流的题能卡掉ISAP,我很高兴。以下还是上面那道题。 +\codeinput[\href{http://poj.org/problem?id=1273}{POJ1273} - Drainage Ditches (ISAP)]{assets/day6/poj1273_ISAP.cpp} +重新写了一遍,1A,我还是很高兴。ISAP比Dinic要注意的点真是多多了,注意第58行的break,这可以大大加速,因为此时源点和汇点已经不会联通了,继续做下去无益。反正我觉得ISAP要是能一遍写出来的话是比Dinic好的。 \end{document} \ No newline at end of file