A - ACM Computer Factory
题目描述:某个工厂可以利用P个部件做一台电脑,有N个加工用的机器,但是每一个机器需要特定的部分才能加工,给你P与N,然后是N行描述机器的最大同时加工数目Q,输入部件要求和输出部件状态,问单位时间内最多可以做多少台机器,再输出运输路线和每一条路线上的待加工机器个数
解题思路:由于机器有最大流量,又是一个点,因此要拆点成一条边,然后构建源点S和汇点T,若一个机器对输入没有任何要求(只有2或0),则从S连一条边到该机器,流量为Q;若一个机器的输出全为1,说明用它加工后一定可以直接得到电脑,从它连到T一条流量也为Q的边;然后考虑是否有一些机器加工之后可以又拿去被其他机器加工呢?遍历每一个机器 i 与剩下的机器 j ,若 i 的输出部件状态符合 j 的输入要求,则从 i 连到 j 一条流量为min(Qi,Qj)的边。当然以上所有的连入和连出不是同一个点,拆点i作为连入点,i+n作为连出点,最后求S到T的最大流后再遍历每一条正向边(跟源点和汇点相连的不要考虑,因为这两个点本身是不存在的),若流量有改变则记录。
如果不拆点这组数据可能过不了
2 4
10 0 0 0 1
10 0 0 0 0
10 0 1 1 1
10 0 1 1 1
答案是
10 1
1 4 10
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
B - Dining
题目描述:给N头牛准备了F种食物和D种饮料,一头牛只能得到一种食物和一种饮料,一种饮料和一种食物也只能被一头牛得到,给你N、F、D,下面N行描述了每一头牛喜爱的食物编号,饮料编号,问最后有几头牛可以得到食物和饮料。
解题思路:由于一个牛可以看成一个点,它只能选一种食物和饮料,因此把牛这个点拆成两部分,左边与喜欢的食物连接,右边与喜欢的饮料连接,左牛与右牛当然是必须要连接的,以上边流量均为1,然后构造源点与汇点,源点到所有饮料一条流量为1的边,所有食物到汇点也一条流量为1的边。最后求源点到汇点的最大流。
代码(这里似乎食物与饮料的位置反了一下,不过不影响程序):
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
C - A Plug for UNIX
题目描述:一些用电器需要充电,给你N种插孔和M种用电器及其对应的插头,最后K种转换器(无限个),问最后有几种用电器不能被充电。
解题思路:构建源点S连到每一个用电器的插头编号一条流量为1的边,由于转换器有无穷多个,因此把转换器的转换前的编号连到转换后的编号一条流量为无穷大的边,再把插孔编号连到汇点T一条流量为1的边,最后求S到T的最大流,用电器个数减去最大流量就是答案,编号怎么判重地记录?map或者不嫌麻烦的用字典树
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
D - Going Home
题目描述:给你一个N*M的点图(人数和房子数自己数,但是房子数会等于人数),每一个人走一格要一美元,每一个格子可以存在多个人,但是一个房子只能最终住一个人(路过没关系)问你如何安排使得最后的每一个人都在房子里且花费最小,输出最小的花费。
解题思路:由于前提是让每一个人都有得住,因此考虑最大流下的最小费用,给每一个人编号,并记录他的坐标,构建源点S连到每一个人一条流量为1的边,每一个人连到每一个房子,流量为人与房子的曼哈顿距离,每一个房子连到汇点T一条流量为1的边,求S到T的最大流小的最小费用。
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
E - Minimum Cost
题目描述:有N个人要买K种东西,又有M种供应商,若最后这N个人均能买到对应数量的物品,则求最小花费;否则输出-1.
解题思路:由于有多种物品处理起来非常麻烦,但是可以发现每一种物品的采购情况对其他物品完全没有影响,又因为物品K只有50,因此可以对每一种物品求买该物品下的最消费用最大流,判断每一次得到的流量是否就是这种物品的总需求量,最后把所有物品的费用加起来即可。题目输入格式简直…………。
对于每一种物品若卖家拥有量大于0,则从源点连到卖家,流量为卖家拥有该物品的量,费用为0;然后若有买家对此物品有需求,则向它连一条边,流量为这个人需要的量,费用为这个物品的单位价钱,最后每一个买家连到汇点T一条边,流量为需要的量,费用为0,然后求K次费用流
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
F - Power Network
题目描述:有n个点,其中有np个发电站,有nc个耗电点,有m条传输电路,求这个电路网的最大电流量。
其中点的编号为 0~n ,由于没有源点和汇点,构建源点S为n,汇点T为n+1。然后从S连到发电站一条边,流量为该发电站单位发电量;从耗电点连到T一条边,流量为单位耗电量;m条传输电路 a 到b传输 c 则从 a 连到 b 流量为 c 的一条边。最后求S到T的最大流,题目看懂了就是很模版的一道题……
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
G - Island Transport
题目描述:给定平面图中N个坐标点和M条边,求最西边的点到最东边的点的最大流量。
解题思路:正规解法其实应该是用什么转化为对偶图再跑最短路,由于没接触过这方面的知识,只好强行最大流了,Dinic低空飞过……
辣鸡代码(还是别看了,去学下正规解法吧):
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 100010;
struct edge
{
int to, nxt, cap;
edge() {}
edge(int To, int Nxt, int Cap): to(To), nxt(Nxt), cap(Cap) {}
};
edge E[N * 4];
int head[N], tot;
int d[N];
queueQ;
inline void init()
{
CLR(head, -1);
tot = 0;
}
inline void add(int s, int t, int cap)
{
E[tot] = edge(t, head[s], cap);
head[s] = tot++;
E[tot] = edge(s, head[t], 0);
head[t] = tot++;
}
int bfs(int s, int t)
{
CLR(d, -1);
d[s] = 0;
while (!Q.empty())
Q.pop();
Q.push(s);
while (!Q.empty())
{
int u = Q.front();
Q.pop();
for (int i = head[u]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == -1 && E[i].cap > 0)
{
d[v] = d[u] + 1;
if (v == t)
return 1;
Q.push(v);
}
}
}
return ~d[t];
}
int dfs(int s, int t, int f)
{
if (s == t || !f)
return f;
int ret = 0;
for (int i = head[s]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == d[s] + 1 && E[i].cap > 0)
{
int df = dfs(v, t, min(f, E[i].cap));
if (df > 0)
{
E[i].cap -= df;
E[i ^ 1].cap += df;
ret += df;
f -= df;
if (!f)
break;
}
}
}
if (!ret)
d[s] = -1;
return ret;
}
int dinic(int s, int t)
{
int ret = 0;
while (bfs(s, t))
ret += dfs(s, t, INF);
return ret;
}
int main(void)
{
int tcase, n, m, i, x, y, west, east, a, b, c;
scanf("%d", &tcase);
while (tcase--)
{
init();
west = INF;
east = -INF;
scanf("%d%d", &n, &m);
int S = 1, T = 1;
for (i = 1; i <= n; ++i)
{
scanf("%d%d", &x, &y);
if (x < west)
{
west = x;
S = i;
}
if (x > east)
{
east = x;
T = i;
}
}
while (m--)
{
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
add(b, a, c);
}
printf("%d\n", dinic(S, T));
}
return 0;
}
H - Food
题目描述:跟那道牛、饮料、食物题目差不多的,只是流量要修改一下,吃的东西可以被多个人选用,因此源点连到食物的边为该食物的份数,饮料连到汇点的边为饮料的份数,其他边连法与B题一样
代码:
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair pii;
typedef long long LL;
const double PI = acos(-1.0);
const int maxn = 807;
const int maxm = 200 + 2 * 200 * 200 + 200 + 7;
struct edge
{
int to, nxt, cap;
edge() {}
edge(int To, int Nxt, int Cap): to(To), nxt(Nxt), cap(Cap) {}
};
edge E[maxm << 1];
int head[maxn], tot;
int d[maxn];
int N, F, D;
char prefer[maxn];
void init()
{
CLR(head, -1);
tot = 0;
}
inline void add(int s, int t, int cap)
{
E[tot] = edge(t, head[s], cap);
head[s] = tot++;
E[tot] = edge(s, head[t], 0);
head[t] = tot++;
}
int bfs(int s, int t)
{
queueQ;
Q.push(s);
CLR(d, -1);
d[s] = 0;
int u, v;
while (!Q.empty())
{
u = Q.front();
Q.pop();
for (int i = head[u]; ~i; i = E[i].nxt)
{
v = E[i].to;
if (d[v] == -1 && E[i].cap > 0)
{
d[v] = d[u] + 1;
if (v == t)
return 1;
Q.push(v);
}
}
}
return ~d[t];
}
int dfs(int s, int t, int f)
{
if (s == t || !f)
return f;
int ret = 0;
for (int i = head[s]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == d[s] + 1 && E[i].cap > 0)
{
int df = dfs(v, t, min(f, E[i].cap));
if (df > 0)
{
E[i].cap -= df;
E[i ^ 1].cap += df;
ret += df;
f -= df;
if (!f)
break;
}
}
}
if (!ret)
d[s] = -1;
return ret;
}
int Dinic(int s, int t)
{
int ret = 0;
while (bfs(s, t))
ret += dfs(s, t, INF);
return ret;
}
inline int getid(const int &n, const char &c)
{
switch (c)
{
case 'F': return n; break;
case 'L': return F + n; break;
case 'R': return F + N + n; break;
case 'D': return F + (N << 1) + n; break;
}
return 0;
}
int main(void)
{
int i, j, c;
while (~scanf("%d%d%d", &N, &F, &D))
{
init();
int S = 0, T = F + (N << 1) + D + 1;
for (i = 1; i <= F; ++i)
{
scanf("%d", &c);
add(S, getid(i, 'F'), c);
}
for (i = 1; i <= D; ++i)
{
scanf("%d", &c);
add(getid(i, 'D'), T, c);
}
for (i = 1; i <= N; ++i)
{
scanf("%s", prefer + 1);
for (j = 1; j <= F; ++j)
if (prefer[j] == 'Y')
add(getid(j, 'F'), getid(i, 'L'), 1);
add(getid(i, 'L'), getid(i, 'R'), 1);
}
for (i = 1; i <= N; ++i)
{
scanf("%s", prefer + 1);
for (j = 1; j <= D; ++j)
if (prefer[j] == 'Y')
add(getid(i, 'R'), getid(j, 'D'), 1);
}
printf("%d\n", Dinic(S, T));
}
return 0;
}
I - Control
题目描述:有一些恐怖份子在城市里活动,他们要把破坏性武器从S送到T,但是你可以派出一些特工去特定城市抓捕他们,有N个城市有M条路,派一个特工去一个城市当然要支付一定费用,如果在保证S到T的路途中抓到这些恐怖分子,求所需支付给特工的最小费用。
解题思路:其实就就是求最小权割点,由于有最大流最小割定理,显然这题又需要拆点了,特工都一样,但是派到不同的城市有不同的费用,因此把城市拆成左右两部分,相连,流量为把特工派到这个程序所需费用,其他的道路边流量都为无穷大。然后求S到T的最大流就是这个最小割的权值了。
代码:
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 410;
const int M = 20010;
struct edge
{
int to, nxt, cap;
edge() {}
edge(int To, int Nxt, int Cap): to(To), nxt(Nxt), cap(Cap) {}
};
edge E[(M + N) << 2];
int head[N], tot;
int d[N];
void init()
{
CLR(head, -1);
tot = 0;
}
inline void add(int s, int t, int cap)
{
E[tot] = edge(t, head[s], cap);
head[s] = tot++;
E[tot] = edge(s, head[t], 0);
head[t] = tot++;
}
int bfs(int s, int t)
{
queueQ;
Q.push(s);
CLR(d, -1);
d[s] = 0;
int u, v;
while (!Q.empty())
{
u = Q.front();
Q.pop();
for (int i = head[u]; ~i; i = E[i].nxt)
{
v = E[i].to;
if (d[v] == -1 && E[i].cap > 0)
{
d[v] = d[u] + 1;
if (v == t)
return 1;
Q.push(v);
}
}
}
return ~d[t];
}
int dfs(int s, int t, int f)
{
if (s == t || !f)
return f;
int ret = 0;
for (int i = head[s]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == d[s] + 1 && E[i].cap > 0)
{
int df = dfs(v, t, min(f, E[i].cap));
if (df > 0)
{
E[i].cap -= df;
E[i ^ 1].cap += df;
ret += df;
f -= df;
if (!f)
break;
}
}
}
if (!ret)
d[s] = -1;
return ret;
}
int Dinic(int s, int t)
{
int ret = 0;
while (bfs(s, t))
ret += dfs(s, t, INF);
return ret;
}
int main(void)
{
int n, m, S, D, a, b, c, i;
while (~scanf("%d%d", &n, &m))
{
init();
scanf("%d%d", &S, &D);
for (i = 1; i <= n; ++i)
{
scanf("%d", &c);
add(i, i + n, c);
add(i + n, i, c);
}
for (i = 0; i < m; ++i)
{
scanf("%d%d", &a, &b);
add(n + a, b, INF);
add(n + b, a, INF);
}
printf("%d\n", Dinic(S, n + D));
}
return 0;
}
J - Sabotage
题目描述:给你N个点M条边,求把点1与2分割开的最小权割边集,并输出这些边。
解题思路:应用最大流最小割定理,求出最大流,然后从直接用残余网络的d数组求出割边集即可
代码:
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 55;
const int M = 510;
struct edge
{
int to, nxt, cap, flag;
edge() {}
edge(int To, int Nxt, int Cap, int Flag): to(To), nxt(Nxt), cap(Cap), flag(Flag) {}
};
edge E[M << 2];
int head[N], tot;
int d[N];
vectorans;
void init()
{
CLR(head, -1);
tot = 0;
ans.clear();
}
inline void add(int s, int t, int c)
{
E[tot] = edge(t, head[s], c, 1);
head[s] = tot++;
E[tot] = edge(s, head[t], 0, -1);
head[t] = tot++;
}
int bfs(int s, int t)
{
queueQ;
Q.push(s);
CLR(d, -1);
d[s] = 0;
int u, v;
while (!Q.empty())
{
u = Q.front();
Q.pop();
for (int i = head[u]; ~i; i = E[i].nxt)
{
v = E[i].to;
if (d[v] == -1 && E[i].cap > 0)
{
d[v] = d[u] + 1;
if (v == t)
return 1;
Q.push(v);
}
}
}
return ~d[t];
}
int dfs(int s, int t, int f)
{
if (s == t || !f)
return f;
int ret = 0;
for (int i = head[s]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == d[s] + 1 && E[i].cap > 0)
{
int df = dfs(v, t, min(f, E[i].cap));
if (df > 0)
{
E[i].cap -= df;
E[i ^ 1].cap += df;
f -= df;
ret += df;
if (!f)
break;
}
}
}
if (!ret)
d[s] = -1;
return ret;
}
void Dinic(int s, int t)
{
while (bfs(s, t))
dfs(s, t, INF);
}
int main(void)
{
int n, m, a, b, c, i, j;
while (~scanf("%d%d", &n, &m) && (n || m))
{
init();
for (i = 0; i < m; ++i)
{
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
add(b, a, c);
}
Dinic(1, 2);
for (a = 1; a <= n; ++a)
{
for (j = head[a]; ~j; j = E[j].nxt)
{
b = E[j].to;
if (d[a] == -1 && d[b] != -1 && E[j].flag == 1)
ans.push_back(pii(a, b));
}
}
for (auto &x : ans)
printf("%d %d\n", x.first, x.second);
putchar('\n');
}
return 0;
}
K - Leapin' Lizards
题目描述:给你一个N*X(X需要自己求)的点图,上面有柱子和空地,柱子上可能有蜥蜴,蜥蜴的最大跳跃曼哈顿距离为D,每一个柱子在蜥蜴离开时均会下沉1个单位(蜥蜴跳上来时不会下沉),求最少多少蜥蜴无法离开这个地图。
解题思路:WA很久发现好几处低级错误,最杯具的时把&&和||搞混了……,由于柱子有最大跳跃量,显然又要拆点了,拆成左边和右边,流量为柱子的原始高度,对与每一根柱子,拆点的右半部分连到汇点流量为无穷大,若发现上面有蜥蜴,则从源点连到柱子左半部分,流量为1,然后遍历每一根柱子若发现它离地图外面的距离小于等于d则把拆点的右半部分连到汇点中,流量为无穷大,再看其他柱子,若柱子 i 可以跳到柱子 j 上,则 i 的右半部分连到 j 的左半部分,流量为1,最后求S到T的最大流,蜥蜴个数减去最大流量就是答案。
代码:
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 25;
const int M = 1e6 + 7;
struct edge
{
int to, nxt, cap;
edge() {}
edge(int To, int Nxt, int Cap): to(To), nxt(Nxt), cap(Cap) {}
};
edge E[M << 1];
int head[N * N * 2], tot;
int d[N * N * 2];
char high[N][N], pos[N][N];
int idhigh[N][N];
void init()
{
CLR(head, -1);
tot = 0;
CLR(high, 0);
CLR(pos, 0);
CLR(idhigh, 0);
}
inline void add(int s, int t, int c)
{
E[tot] = edge(t, head[s], c);
head[s] = tot++;
E[tot] = edge(s, head[t], 0);
head[t] = tot++;
}
int bfs(int s, int t)
{
CLR(d, -1);
d[s] = 0;
queueq;
q.push(s);
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = head[u]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == -1 && E[i].cap > 0)
{
d[v] = d[u] + 1;
if (v == t)
return 1;
q.push(v);
}
}
}
return ~d[t];
}
int dfs(int s, int t, int f)
{
if (s == t || !f)
return f;
int ret = 0;
for (int i = head[s]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == d[s] + 1 && E[i].cap > 0)
{
int df = dfs(v, t, min(f, E[i].cap));
if (df > 0)
{
E[i].cap -= df;
E[i ^ 1].cap += df;
f -= df;
ret += df;
if (!f)
break;
}
}
}
if (!ret)
d[s] = -1;
return ret;
}
int dinic(int s, int t)
{
int ret = 0;
while (bfs(s, t))
ret += dfs(s, t, INF);
return ret;
}
int main(void)
{
int tcase, n, m, i, j, d, ii, jj;
scanf("%d", &tcase);
for (int q = 1; q <= tcase; ++q)
{
init();
scanf("%d%d", &n, &d);
for (i = 0; i < n; ++i)
scanf("%s", high[i]);
for (i = 0; i < n; ++i)
scanf("%s", pos[i]);
m = strlen(pos[0]);
int id = 0;
for (i = 0; i < n; ++i)
for (j = 0; j < m; ++j)
if (high[i][j] != '0')
idhigh[i][j] = ++id;
int S = 0, T = id * 2 + 1;
int Lizard = 0;
for (i = 0; i < n; ++i)
{
for (j = 0; j < m; ++j)
{
if (pos[i][j] == 'L')
{
add(S, idhigh[i][j], 1);
++Lizard;
}
if (high[i][j] != '0')
{
add(idhigh[i][j], idhigh[i][j] + id, high[i][j] - '0');
if (i + 1 <= d || j + 1 <= d || n - i <= d || m - j <= d)
add(id + idhigh[i][j], T, INF);
for (ii = 0; ii < n; ++ii)
{
for (jj = 0; jj < m; ++jj)
{
if (ii == i && jj == j)
continue;
if (idhigh[ii][jj] && abs(i - ii) + abs(j - jj) <= d)
add(id + idhigh[i][j], idhigh[ii][jj], INF);
}
}
}
}
}
printf("Case #%d: ", q);
int ans = Lizard - dinic(S, T);
if (!ans)
puts("no lizard was left behind.");
else if (ans == 1)
puts("1 lizard was left behind.");
else
printf("%d lizards were left behind.\n", ans);
}
return 0;
}
M - Escape
题目描述:有N个人,M个星球,他们要逃亡到这些星球上,但是每一个人只能去适宜的星球,星球本身也有容量限制,求最后可以逃走多少人
解题思路:若每一个人连到一个星球上则可能有$(100000+100000*10+10)*2$条边,可能会超时,但是由于星球数只有10个,因此一个人的选择状态最多有$2^{10}=1024$种,每一种状态里的人都是完全等价的,因此先把每一个人的选择状态储存下来,然后统计每一种选择状态下的人数,从源点连到某一种选择状态,流量为该状态的人数;从该种选择状态连到状态中适合的星球,流量为无穷大;再从星球连到汇点流量为星球的限制人数,最后求S到T的最大流量就是答案。
代码:
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = (1 << 10) + 10 + 7;
const int M = N + N * 10 + 10 + 7;
struct edge
{
int to, nxt, cap;
edge() {}
edge(int To, int Nxt, int Cap): to(To), nxt(Nxt), cap(Cap) {}
};
edge E[M << 1];
int head[N], tot;
int d[N];
int cnt_st[N];
void init()
{
CLR(head, -1);
tot = 0;
CLR(cnt_st, 0);
}
inline void add(int s, int t, int c)
{
E[tot] = edge(t, head[s], c);
head[s] = tot++;
E[tot] = edge(s, head[t], 0);
head[t] = tot++;
}
int bfs(int s, int t)
{
CLR(d, -1);
d[s] = 0;
queueq;
q.push(s);
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = head[u]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == -1 && E[i].cap > 0)
{
d[v] = d[u] + 1;
if (v == t)
return 1;
q.push(v);
}
}
}
return ~d[t];
}
int dfs(int s, int t, int f)
{
if (s == t || !f)
return f;
int ret = 0;
for (int i = head[s]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == d[s] + 1 && E[i].cap > 0)
{
int df = dfs(v, t, min(f, E[i].cap));
if (df > 0)
{
E[i].cap -= df;
E[i ^ 1].cap += df;
ret += df;
f -= df;
if (!f)
break;
}
}
}
if (!ret)
d[s] = -1;
return ret;
}
int Dinic(int s, int t)
{
int ret = 0;
while (bfs(s, t))
ret += dfs(s, t, INF);
return ret;
}
int main(void)
{
int n, m, i, j, k, c;
while (~scanf("%d%d", &n, &m))
{
init();
int maxm_st = -INF;
for (i = 0; i < n; ++i)
{
int st = 0;
for (j = 0; j < m; ++j)
{
scanf("%d", &k);
st = (st << 1) + k;
}
++cnt_st[st];
if (st > maxm_st)
maxm_st = st;
}
int S = maxm_st + m + 1, T = maxm_st + m + 2;
bitset<12>tmp;
for (i = 0; i <= maxm_st; ++i)
{
if (cnt_st[i])
{
add(S, i, cnt_st[i]);
tmp = i;
for (k = m - 1; k >= 0; --k)
{
if (tmp[k])
add(i, maxm_st + (m - 1 - k) + 1, INF);
}
}
}
for (i = 0; i < m; ++i)
{
scanf("%d", &c);
add(maxm_st + i + 1, T, c);
}
puts(Dinic(S, T) == n ? "YES" : "NO");
}
return 0;
}
N - Marriage Match II
题目描述:有N个男孩和N个女孩玩一种游戏,女孩可以选择没有跟她吵过架的男孩结婚,或者跟她朋友没吵过架的(只要存在一个朋友没吵过架就行),即抢她朋友的男朋友,一轮下来每一个人都要选到一个男孩,求这样可以玩几轮。
解题思路:可以考虑这样一个情况,若成功玩了一轮,则说明每一个男孩都有一个女孩对应,此图为完美匹配,对应的最大流为n,那么这样二分枚举轮数k,然后求最大流是否等于n*k。即每一个人都有k个流量汇聚到汇点。
代码:
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 110;
const int M = N + N * N + N;
struct edge
{
int to, nxt, cap;
edge() {}
edge(int To, int Nxt, int Cap): to(To), nxt(Nxt), cap(Cap) {}
};
edge E[M << 2];
int head[N << 1], tot;
int d[N << 1];
int girl[N];
int linker[N][N];
void init()
{
for (int i = 0; i < N; ++i)
girl[i] = i;
CLR(linker, -1);
}
void reset()
{
CLR(head, -1);
tot = 0;
}
int Find(int n)
{
if (n == girl[n])
return n;
return girl[n] = Find(girl[n]);
}
void joint(int a, int b)
{
a = Find(a), b = Find(b);
if (a != b)
girl[a] = b;
}
inline void add(int s, int t, int cap)
{
E[tot] = edge(t, head[s], cap);
head[s] = tot++;
E[tot] = edge(s, head[t], 0);
head[t] = tot++;
}
int bfs(int s, int t)
{
CLR(d, -1);
d[s] = 0;
queueq;
q.push(s);
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = head[u]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == -1 && E[i].cap > 0)
{
d[v] = d[u] + 1;
if (v == t)
return 1;
q.push(v);
}
}
}
return ~d[t];
}
int dfs(int s, int t, int f)
{
if (s == t || !f)
return f;
int ret = 0;
for (int i = head[s]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == d[s] + 1 && E[i].cap > 0)
{
int df = dfs(v, t, min(f, E[i].cap));
if (df > 0)
{
E[i].cap -= df;
E[i ^ 1].cap += df;
f -= df;
ret += df;
if (!f)
break;
}
}
}
if (!ret)
d[s] = -1;
return ret;
}
int Dinic(int s, int t)
{
int ret = 0;
while (bfs(s, t))
ret += dfs(s, t, INF);
return ret;
}
int main(void)
{
int tcase, n, m, f, a, b, i, j;
scanf("%d", &tcase);
while (tcase--)
{
init();
scanf("%d%d%d", &n, &m, &f);
for (i = 0; i < m; ++i)
{
scanf("%d%d", &a, &b);
linker[a][b] = 1;
}
for (i = 0; i < f; ++i)
{
scanf("%d%d", &a, &b);
joint(a, b);
int fa = Find(a);
for (j = 1; j <= n; ++j)
{
if (linker[a][j] != -1 || linker[b][j] != -1 || linker[fa][j] != -1)
linker[a][j] = linker[b][j] = linker[fa][j] = 1;
}
}
int L = 0, R = n;
int ans = 0;
int S = 0, T = n * 2 + 1;
while (L <= R)
{
int mid = MID(L, R);
reset();
for (i = 1; i <= n; ++i)
{
add(S, i, mid); //n
add(n + i, T, mid); //n
int fi = Find(i);
for (j = 1; j <= n; ++j)
{
if (linker[fi][j] != -1 || linker[i][j] != -1)
add(i, n + j, 1); //n*n
}
}
int Flow = Dinic(S, T);
if (Flow == mid * n)
{
L = mid + 1;
ans = mid;
}
else
R = mid - 1;
}
printf("%d\n", ans);
}
return 0;
}
O - Marriage Match IV
题目描述:一个人要去一个指定地点,但是每一条边都只能走一次,他又想走最短路线,那么他可以去几次呢?
解题思路:若用网络流来解可以发现:一条路只能走一次说明这条路的容量为1,只要求S到T的最大流即可,每一条边流量均为1,因为是边完全不重复的最短路,因此任意的边有影响对于整个网络均可能有影响
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include