您的位置:首页 > 理论基础 > 计算机网络

2017 01 05 校内网络流小测

2017-01-05 17:00 183 查看
鉴于这几日大家都有学习网络流知识,LincHpin大爷决定考一考我们网络流,然后,我就爆零了,233……

T1 Seal

想了好久,也不知道怎么建图,然后心态就崩了,就刷BZOJ去了……

考场上写的随机化和错误贪心都被大爷卡了,T_T,Orz YYH,YZY,GZZ三位AK大佬。

首先,以一个恶魔为中心,形成一个直角的充要条件是,在左右相邻格子中选一个,在上下相邻格子中选一个。

然后发现,对于那些水晶格,同为奇数行的总是不会同时被选,同为偶数行的也不会同时被选(此指被选中流向同一个恶魔格子)。就是说,水晶格可以按照所在行(或者列,随便啦)的奇偶进行黑白染色,每个恶魔总是需要匹配一个黑水晶和一个白水晶。

所以把黑水晶放在左侧,每个和源点之间有容量为1的边;白水晶放在右侧,每个和汇点之间也有容量为1的边。中间是恶魔点,每个恶魔拆成两个点,分别和相邻的黑、白水晶相连,中间是容量为1的边。跑最大流就行了。

#include <cstdio>

inline int nextChar(void) {
const int siz = 1024;

static char buf[siz];
static char *hd = buf + siz;
static char *tl = buf + siz;

if (hd == tl)
fread(hd = buf, 1, siz, stdin);

return *hd++;
}

inline int nextInt(void) {
register int ret = 0;
register int neg = false;
register int bit = nextChar();

for (; bit < 48; bit = nextChar())
if (bit == '-')neg ^= true;

for (; bit > 47; bit = nextChar())
ret = ret * 10 + bit - 48;

return neg ? -ret : ret;
}

int n, m;

bool g[55][55];

inline bool next(void)
{
char c = nextChar();

while (c != '.' && c != 'X')
c = nextChar();

return c == '.';
}

const int siz = 500005;
const int inf = 1000000007;

int tot;
int s, t;
int hd[siz];
int to[siz];
int fl[siz];
int nt[siz];

inline void add(int u, int v, int f)
{
nt[tot] = hd[u]; to[tot] = v; fl[tot] = f; hd[u] = tot++;
nt[tot] = hd[v]; to[tot] = u; fl[tot] = 0; hd[v] = tot++;
}

int dep[siz];

inline bool bfs(void)
{
static int que[siz], head, tail;

for (int i = s; i <= t; ++i)dep[i] = 0;

dep[que[head = 0] = s] = tail = 1;

while (head != tail)
{
int u = que[head++], v;

for (int i = hd[u]; ~i; i = nt[i])
if (!dep[v = to[i]] && fl[i])
dep[que[tail++] = v] = dep[u] + 1;
}

return dep[t];
}

int cur[siz];

inline int min(int a, int b)
{
return a < b ? a : b;
}

int dfs(int u, int f)
{
if (u == t || !f)
return f;

int used = 0, flow, v;

for (int i = cur[u]; ~i; i = nt[i])
if (dep[v = to[i]] == dep[u] + 1 && fl[i])
{
flow = dfs(v, min(fl[i], f - used));

used += flow;
fl[i] -= flow;
fl[i^1] += flow;

if (used == f)
return f;

if (fl[i])
cur[u] = i;
}

if (!used)
dep[u] = 0;

return used;
}

inline int maxFlow(void)
{
int maxFlow = 0, newFlow;

while (bfs())
{
for (int i = s; i <= t; ++i)
cur[i] = hd[i];

while (newFlow = dfs(s, inf))
maxFlow += newFlow;
}

return maxFlow;
}

inline int pos(int x, int y)
{
return m * (x - 1) + y;
}

const int mv[4][2] =
{
{0, +1},
{0, -1},
{-1, 0},
{+1, 0}
};

signed main(void)
{
freopen("Seal.in", "r", stdin);
freopen("Seal.out", "w", stdout);

n = nextInt();
m = nextInt();

for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
g[i][j] = next();

s = 0, t = n * m * 2 + 1;

for (int i = s; i <= t; ++i)
hd[i] = -1;

for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (g[i][j])
{
if ((i + j) & 1)
{
if (i & 1)
add(s, pos(i, j), 1);
else
add(pos(i, j), t, 1);
}
else
{
for (int k = 0; k < 4; ++k)
{
int x = i + mv[k][0];
int y = j + mv[k][1];

if (x < 1 || x > n)
continue;
if (y < 1 || y > m)
continue;
if (!g[x][y])
continue;

if (x & 1)
add(pos(x, y), pos(i, j), 1);
else
add(pos(i, j) + n*m, pos(x, y), 1);

add(pos(i, j), pos(i, j) + n*m, 1);
}
}
}

printf("%d\n", maxFlow());

return fclose(stdin), fclose(stdout), 0;
}


T2 Repair

依旧不会做,然后听LH大爷讲题解,讲完就怀疑自己智商了……

有点像是可行流问题(本来就是可行流好嘛),只是需要分类讨论一下。

对于一条边,如果一开始流量$F$大于$C$,即$F \gt C$,那么有如下情况:

如果最后流量$X$大于$F$,即$X \gt F \gt C$,那么有$fare=X-F+X-C$,如果设$K=F-C$,有$fare=K+2(X-F)$.

如果最后流量$X$在$F$和$C$之间,即$F \gt X \gt C$,那么有$fare=F-X+X-C=K$.

如果最后流量$X$小于$C$,即$F \gt C \gt X$,那么有$fare=F-X=K+C-X$.

不难总结出对于这类边的建新边方式:

先将$K$直接统计到答案之中,然后从$u$向$v$连容量为$INF$的边,费用为$2$,从$v$向$u$连容量为$F-C$的边,费用为$0$,从$v$向$u$连容量为$C$的边,费用为$1$。

对于一条边,如果一开始流量$F$小于$C$,即$F \lt C$,那么有如下情况:

如果最后流量$X$大于$C$,即$X \gt C \gt F$,那么有$fare=X-C+X-F$。

如果最后流量$X$在$C$和$F$之间,即$C \gt X \gt F$,那么有$fare=X-F$。

如果最后流量$X$小于$F$,即$C \gt F \gt X$,那么有$fare=F-X$。

不难总结出对这类边的建新边方式:

从$u$向$v$连容量为$C-F$的边,费用为$1$,从$u$向$v$连容量为$INF$的边,费用为$2$,从$v$向$u$连容量为$F$的边,费用为$1$。

最后为了满足流量守恒,设立新的源汇点,源点向流入量多的点连边,流出量多的点向汇点连边,原汇点向原源点连无穷的边,跑最小费用最大流即可。

#include <cstdio>

inline int nextChar(void) {
const int siz = 1024;

static char buf[siz];
static char *hd = buf + siz;
static char *tl = buf + siz;

if (hd == tl)
fread(hd = buf, 1, siz, stdin);

return *hd++;
}

inline int nextInt(void) {
register int ret = 0;
register int neg = false;
register int bit = nextChar();

for (; bit < 48; bit = nextChar())
if (bit == '-')neg ^= true;

for (; bit > 47; bit = nextChar())
ret = ret * 10 + bit - 48;

return neg ? -ret : ret;
}

const int siz = 5000005;
const int inf = 1000000007;

int n, m;
int answer;
int del[siz];

int tot;
int s, t;
int hd[siz];
int to[siz];
int fl[siz];
int vl[siz];
int nt[siz];

inline void add(int u, int v, int f, int w)
{
//    printf("add %d %d %d %d\n", u, v, f, w);
nt[tot] = hd[u]; to[tot] = v; fl[tot] = f; vl[tot] = +w; hd[u] = tot++;
nt[tot] = hd[v]; to[tot] = u; fl[tot] = 0; vl[tot] = -w; hd[v] = tot++;
}

int dep[siz];

inline bool bfs(void)
{
static int que[siz], head, tail;

for (int i = s; i <= t; ++i)dep[i] = 0;

dep[que[head = 0] = s] = tail = 1;

while (head != tail)
{
int u = que[head++], v;

for (int i = hd[u]; ~i; i = nt[i])
if (!dep[v = to[i]] && fl[i] && !vl[i])
dep[que[tail++] = v] = dep[u] + 1;
}

return dep[t];
}

int cur[siz];

inline int min(int a, int b)
{
return a < b ? a : b;
}

int dfs(int u, int f)
{
if (u == t || !f)
return f;

int used = 0, flow, v;

for (int i = hd[u]; ~i; i = nt[i])
if (dep[v = to[i]] == dep[u] + 1 && fl[i] && !vl[i])
{
flow = dfs(v, min(fl[i], f - used));

used += flow;
fl[i] -= flow;
fl[i^1] += flow;

if (used == f)
return f;

if (fl[i])
cur[u] = i;
}

if (!used)
dep[u] = 0;

return used;
}

inline void maxFlow(void)
{
while (bfs())
while (dfs(s, inf));
}

int dis[siz];
int pre[siz];

inline bool spfa(void)
{
static int que[siz];
static int inq[siz];
static int head, tail;

for (int i = s; i <= t; ++i)
inq[i] = 0, dis[i] = inf;

head = 0, tail = 0;
que[tail++] = s;
pre[s] = -1;
dis[s] = 0;

while (head != tail)
{
int u = que[head++], v; inq[u] = 0;

for (int i = hd[u]; ~i; i = nt[i])
if (dis[v = to[i]] > dis[u] + vl[i] && fl[i])
{
dis[v] = dis[u] + vl[i], pre[v] = i ^ 1;
if (!inq[v])
inq[que[tail++] = v] = 1;
}
}

return dis[t] < inf;
}

inline void minCost(void)
{
while (spfa())
{
int flow = inf;

for (int i = pre[t]; ~i; i = pre[to[i]])
if (fl[i ^ 1] < flow)flow = fl[i ^ 1];

for (int i = pre[t]; ~i; i = pre[to[i]])
fl[i] += flow, fl[i ^ 1] -= flow;

answer += dis[t] * flow;
}
}

signed main(void)
{
freopen("Repair.in", "r", stdin);
freopen("Repair.out", "w", stdout);

n = nextInt();
m = nextInt();

s = 0, t = n + 1;

for (int i = s; i <= t; ++i)
hd[i] = -1;

for (int i = 1; i <= m; ++i)
{
int u = nextInt();
int v = nextInt();
int c = nextInt();
int f = nextInt();

del[u] += f;
del[v] -= f;

if (f <= c)
{
add(u, v, c - f, 1);
add(u, v, inf, 2);
add(v, u, f, 1);
}
else
{
answer += f - c;
add(u, v, inf, 2);
add(v, u, f - c, 0);
add(v, u, c, 1);
}
}

add(n, 1, inf, 0);

for (int i = 1; i <= n; ++i)
if (del[i] < 0)
add(s, i, -del[i], 0);
else if (del[i] > 0)
add(i, t, del[i], 0);

maxFlow();

minCost();

printf("%d\n", answer);

return fclose(stdin), fclose(stdout), 0;
}


T3 Flow

依旧不会,看到$G(f)$函数就是mengbi的。

GZZ神犇说,MCMF中每次增广出来的路径,就是$C(f)$关于$F(f)$函数中的一段一次函数…… $OTZ GZZ$

然后,分步跑出来每一段的函数,求函数上距离(maxFlow,0)点的最近距离即可,我怎么这么蠢,23333

#include <cstdio>

template <class T>
inline T min(T a, T b)
{
return a < b ? a : b;
}

template <class T>
T gcd(T a, T b)
{
return b ? gcd(b, a % b) : a;
}

template <class T>
inline T sqr(T x)
{
return x * x;
}

#define int long long

inline int nextChar(void) {
const int siz = 1024;

static char buf[siz];
static char *hd = buf + siz;
static char *tl = buf + siz;

if (hd == tl)
fread(hd = buf, 1, siz, stdin);

return *hd++;
}

inline int nextInt(void) {
register int ret = 0;
register int neg = false;
register int bit = nextChar();

for (; bit < 48; bit = nextChar())
if (bit == '-')neg ^= true;

for (; bit > 47; bit = nextChar())
ret = ret * 10 + bit - 48;

return neg ? -ret : ret;
}

const int siz = 500005;
const int inf = 1000000007;

int n, m;

int tot;
int s, t;
int hd[siz];
int nt[siz];
int fl[siz];
int vl[siz];
int to[siz];

inline void add(int u, int v, int f, int w)
{
nt[tot] = hd[u]; to[tot] = v; vl[tot] = +w; fl[tot] = f; hd[u] = tot++;
nt[tot] = hd[v]; to[tot] = u; vl[tot] = -w; fl[tot] = 0; hd[v] = tot++;
}

int dep[siz];

inline bool bfs(void)
{
static int que[siz], head, tail;

for (int i = 1; i <= n; ++i)dep[i] = 0;

dep[que[head = 0] = s] = tail = 1;

while (head != tail)
{
int u = que[head++], v;

for (int i = hd[u]; ~i; i = nt[i])
if (!dep[v = to[i]] && fl[i])
dep[que[tail++] = v] = dep[u] + 1;
}

return dep[t];
}

int cur[siz];

int dfs(int u, int f)
{
if (u == t || !f)
return f;

int used = 0, flow, v;

for (int i = cur[u]; ~i; i = nt[i])
if (dep[v = to[i]] == dep[u] + 1 && fl[i])
{
flow = dfs(v, min(fl[i], f - used));

used += flow;
fl[i] -= flow;
fl[i^1] += flow;

if (used == f)
return f;

if (fl[i])
cur[u] = i;
}

if (!used)
dep[u] = 0;

return used;
}

inline int Dinic(void)
{
int maxFlow = 0, newFlow;

while (bfs())
{
for (int i = 1; i <= n; ++i)
cur[i] = hd[i];

while (newFlow = dfs(s, inf))
maxFlow += newFlow;
}

return maxFlow;
}

int dis[siz];
int pre[siz];

inline bool spfa(void)
{
static int que[siz];
static int inq[siz];
static int head, tail;

for (int i = 1; i <= n; ++i)
dis[i] = inf, inq[i] = 0;

head = 0, tail = 0;
que[tail++] = s;
pre[s] = -1;
dis[s] = 0;
inq[s] = 1;

while (head != tail)
{
int u = que[head++], v; inq[u] = 0;

for (int i = hd[u]; ~i; i = nt[i])
if (fl[i] && dis[v = to[i]] > dis[u] + vl[i])
{
dis[v] = dis[u] + vl[i], pre[v] = i ^ 1;
if (!inq[v])inq[que[tail++] = v] = 1;
}
}

return dis[t] < inf;
}

signed main(void)
{
freopen("Flow.in", "r", stdin);
freopen("Flow.out", "w", stdout);

n = nextInt();
m = nextInt();
s = nextInt();
t = nextInt();

for (int i = 1; i <= n; ++i)
hd[i] = -1;

for (int i = 1; i <= m; ++i)
{
int x = nextInt();
int y = nextInt();
int u = nextInt();
int c = nextInt();
add(x, y, u, c);
}

int maxFlow = Dinic();

for (int i = 0; i < tot; i += 2)
fl[i] += fl[i ^ 1], fl[i ^ 1] = 0;

int C = 0, F = maxFlow;

while (spfa())
{
int w = dis[t];

int flow = inf;

for (int i = pre[t]; ~i; i = pre[to[i]])
if (fl[i ^ 1] < flow)
flow = fl[i ^ 1];

if (F - w * C >= flow * (sqr(w) + 1))
{
for (int i = pre[t]; ~i; i = pre[to[i]])
fl[i] += flow, fl[i ^ 1] -= flow;

F -= flow, C += flow * dis[t];
}
else
{
if (F > w * C)
{
int r = sqr(w * F + C);
int d = sqr(w) + 1;
int g = gcd(r, d);
printf("%lld/%lld\n", r / g, d / g);
}
else
printf("%lld/%lld\n", sqr(C) + sqr(F), 1LL);

goto end;
}
}

printf("%lld/%lld\n", sqr(C) + sqr(F), 1LL);

end:return fclose(stdin), fclose(stdout), 0;
}


@Author: YouSiki
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: