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 <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <sstream>
#include <numeric>
#include <cstring>
#include <bitset>
#include <string>
#include <deque>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
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<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 55;
const int M = N + N * N + N;
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) {}
};
struct info
{
int id, q;
int in[11], out[11];
info()
{
for (int i = 0; i < 11; ++i)
in[i] = out[i] = 0;
}
};
info node[N];
edge E[M << 1];
int head[N << 1], tot;
int d[N << 1]; void init()
{
CLR(head, -1);
tot = 0;
}
inline void add(int s, int t, int cap, int flag)
{
E[tot] = edge(t, head[s], cap, flag);
head[s] = tot++;
E[tot] = edge(s, head[t], 0, 0);
head[t] = tot++;
}
int bfs(int s, int t)
{
CLR(d, -1);
d[s] = 0;
queue<int>q;
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 p, n, i, j, k;
while (~scanf("%d%d", &p, &n))
{
init();
int S = 0, T = 2 * n + 1;
for (i = 1; i <= n; ++i)
{
scanf("%d", &node[i].q);
bool sflag = true, flagt = true;
for (j = 1; j <= p; ++j)
{
scanf("%d", &node[i].in[j]);
if (node[i].in[j] == 1)
sflag = false;
}
for (j = 1; j <= p; ++j)
{
scanf("%d", &node[i].out[j]);
if (!node[i].out[j])
flagt = false;
}
if (sflag)
add(S, i, node[i].q, 0); //n
if (flagt)
add(n + i, T, node[i].q, 0); //n
add(i, n + i, node[i].q, 0);
}
for (i = 1; i <= n; ++i)
{
for (j = 1; j <= n; ++j)
{
if (i == j)
continue;
int ok = true;
for (k = 1; k <= p; ++k)
{
if ((node[j].in[k] == 1 && !node[i].out[k]) || (!node[j].in[k] && node[i].out[k] == 1))
{
ok = false;
break;
}
}
if (ok)
add(n + i, j, min<int>(node[i].q, node[j].q), 1); //n*n
}
}
int max_flow = Dinic(S, T);
vector<edge>ans;
for (int a = n + 1; a <= n << 1; ++a)
{
for (j = head[a]; ~j; j = E[j].nxt)
{
if (E[j].flag && E[j ^ 1].cap)
{
int b = E[j].to;
ans.push_back(edge(a - n, b, E[j ^ 1].cap, 0));
}
}
}
int C_edges = ans.size();
printf("%d %d\n", max_flow, C_edges);
for (i = 0; i < C_edges; ++i)
printf("%d %d %d\n", ans[i].to, ans[i].nxt, ans[i].cap);
}
return 0;
}

B - Dining

题目描述:给N头牛准备了F种食物和D种饮料,一头牛只能得到一种食物和一种饮料,一种饮料和一种食物也只能被一头牛得到,给你N、F、D,下面N行描述了每一头牛喜爱的食物编号,饮料编号,问最后有几头牛可以得到食物和饮料。

解题思路:由于一个牛可以看成一个点,它只能选一种食物和饮料,因此把牛这个点拆成两部分,左边与喜欢的食物连接,右边与喜欢的饮料连接,左牛与右牛当然是必须要连接的,以上边流量均为1,然后构造源点与汇点,源点到所有饮料一条流量为1的边,所有食物到汇点也一条流量为1的边。最后求源点到汇点的最大流。

代码(这里似乎食物与饮料的位置反了一下,不过不影响程序):

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <sstream>
#include <numeric>
#include <cstring>
#include <bitset>
#include <string>
#include <deque>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
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<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int maxn = 410;
const int M = 210 * 100;
struct edge
{
int to, nxt;
int cap;
};
edge E[M << 1];
int head[maxn], tot;
int d[maxn];
int N, F, D; void init()
{
CLR(head, -1);
tot = 0;
}
inline void add(int s, int t, int cap)
{
E[tot].to = t;
E[tot].cap = cap;
E[tot].nxt = head[s];
head[s] = tot++; E[tot].to = s;
E[tot].cap = 0;
E[tot].nxt = head[t];
head[t] = tot++;
}
int bfs(int s, int t)
{
queue<int>Q;
Q.push(s);
CLR(d, INF);
d[s] = 0;
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] == INF && E[i].cap > 0)
{
d[v] = d[u] + 1;
if (v == t)
return 1;
Q.push(v);
}
}
}
return d[t] != INF;
}
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;
}
inline int Drink(const int &n)
{
return n;
}
inline int Lcow(const int &n)
{
return D + n;
}
inline int Rcow(const int &n)
{
return D + N + n;
}
inline int Food(const int &n)
{
return D + (N << 1) + n;
} int main(void)
{
int i, fi, di, id;
while (~scanf("%d%d%d", &N, &F, &D))
{
init();
int S = 0, T = D + (N << 1) + F + 1;
for (i = 1; i <= N; ++i)
{
add(Lcow(i), Rcow(i), 1);
scanf("%d%d", &fi, &di);
while (fi--)
{
scanf("%d", &id);
add(Rcow(i), Food(id), 1);
}
while (di--)
{
scanf("%d", &id);
add(Drink(id), Lcow(i), 1);
}
}
for (i = 1; i <= D; ++i)
add(S, Drink(i), 1);
for (i = 1; i <= F; ++i)
add(Food(i), T, 1);
printf("%d\n", dinic(S, T));
}
return 0;
}

C - A Plug for UNIX

题目描述:一些用电器需要充电,给你N种插孔和M种用电器及其对应的插头,最后K种转换器(无限个),问最后有几种用电器不能被充电。

解题思路:构建源点S连到每一个用电器的插头编号一条流量为1的边,由于转换器有无穷多个,因此把转换器的转换前的编号连到转换后的编号一条流量为无穷大的边,再把插孔编号连到汇点T一条流量为1的边,最后求S到T的最大流,用电器个数减去最大流量就是答案,编号怎么判重地记录?map或者不嫌麻烦的用字典树

代码:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <sstream>
#include <numeric>
#include <cstring>
#include <bitset>
#include <string>
#include <deque>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
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<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 610;
const int maxm = N * N * 2;
struct edge
{
int to, nxt;
int cap;
};
edge E[maxm << 1];
int head[N], tot;
int d[N];
map<string, int>st;
vector<int>L;
vector<pii>M, R; inline void init()
{
CLR(head, -1);
tot = 0;
st.clear();
L.clear();
M.clear();
R.clear();
}
inline void add(int s, int t, int c)
{
E[tot].to = t;
E[tot].cap = c;
E[tot].nxt = head[s];
head[s] = tot++; E[tot].to = s;
E[tot].cap = 0;
E[tot].nxt = head[t];
head[t] = tot++;
}
int bfs(int s, int t)
{
queue<int>Q;
CLR(d, INF);
d[s] = 0;
Q.push(s);
while (!Q.empty())
{
int now = Q.front();
Q.pop();
for (int i = head[now]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == INF && E[i].cap > 0)
{
d[v] = d[now] + 1;
if (v == t)
return 1;
Q.push(v);
}
}
}
return d[t] != INF;
}
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)
{
FAST_IO
int n, m, k, id, i;
string a, b;
while (cin >> n)
{
id = 0;
init();
for (i = 0; i < n; ++i)
{
cin >> a;
if (!st[a])
{
st[a] = ++id;
L.push_back(id);
}
}
cin >> m;
for (i = 0; i < m; ++i)
{
cin >> a >> b;
int A, B;
if (!st[a])
st[a] = ++id;
A = st[a];
if (!st[b])
st[b] = ++id;
B = st[b];
M.push_back(pii(A, B));
}
cin >> k;
for (i = 0; i < k; ++i)
{
cin >> a >> b;
int A, B;
if (!st[a])
st[a] = ++id;
A = st[a];
if (!st[b])
st[b] = ++id;
B = st[b];
R.push_back(pii(A, B));
}
int S = 0, T = id + 1; int sz = M.size();
for (i = 0; i < sz; ++i)
add(S, M[i].second, 1); sz = R.size();
for (i = 0; i < sz; ++i)
add(R[i].first, R[i].second, INF); sz = L.size();
for (i = 0; i < sz; ++i)
add(L[i], T, 1); cout << m - dinic(S, T) << endl;
}
return 0;
}

D - Going Home

题目描述:给你一个N*M的点图(人数和房子数自己数,但是房子数会等于人数),每一个人走一格要一美元,每一个格子可以存在多个人,但是一个房子只能最终住一个人(路过没关系)问你如何安排使得最后的每一个人都在房子里且花费最小,输出最小的花费。

解题思路:由于前提是让每一个人都有得住,因此考虑最大流下的最小费用,给每一个人编号,并记录他的坐标,构建源点S连到每一个人一条流量为1的边,每一个人连到每一个房子,流量为人与房子的曼哈顿距离,每一个房子连到汇点T一条流量为1的边,求S到T的最大流小的最小费用。

代码:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <sstream>
#include <numeric>
#include <cstring>
#include <bitset>
#include <string>
#include <deque>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
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<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 110; struct edge
{
int to, nxt;
int cost, cap;
};
struct info
{
int x, y, id;
info() {}
info(int xx, int yy, int idd): x(xx), y(yy), id(idd) {}
int cal(const info &b)
{
return abs(x - b.x) + abs(y - b.y);
}
};
edge E[N * N * 2];
int head[N << 1], tot;
int dis[N << 1], pre[N << 1], path[N << 1];
int mc, mf;
bitset<N> vis;
char pos[N][N];
info man[N], house[N]; inline void init()
{
CLR(head, -1);
tot = 0;
mc = mf = 0;
}
inline void add(int s, int t, int cap, int cost)
{
E[tot].to = t;
E[tot].cap = cap;
E[tot].cost = cost;
E[tot].nxt = head[s];
head[s] = tot++; E[tot].to = s;
E[tot].cap = 0;
E[tot].cost = -cost;
E[tot].nxt = head[t];
head[t] = tot++;
}
int spfa(int s, int t)
{
queue<int>Q;
Q.push(s);
CLR(dis, INF);
vis.reset();
Q.push(s);
dis[s] = 0;
path[s] = 0;
pre[s] = -1;
vis[s] = true;
while (!Q.empty())
{
int now = Q.front();
Q.pop();
vis[now] = false;
for (int i = head[now]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (dis[v] > dis[now] + E[i].cost && E[i].cap > 0)
{
dis[v] = dis[now] + E[i].cost;
pre[v] = now;
path[v] = i;
if (!vis[v])
{
vis[v] = true;
Q.push(v);
}
}
}
}
return dis[t] != INF;
}
void MCMF(int s, int t)
{
int i;
while (spfa(s, t))
{
int f = INF;
for (i = t; i != s; i = pre[i])
f = min<int>(f, E[path[i]].cap);
for (i = t; i != s; i = pre[i])
{
E[path[i]].cap -= f;
E[path[i] ^ 1].cap += f;
}
mf += f;
mc += f * dis[t];
}
}
int main(void)
{
int n, m, i, j;
while (~scanf("%d%d", &n, &m) && (n || m))
{
init();
int cntm = 0, cnth = 0;
for (i = 0; i < n; ++i)
{
scanf("%s", pos[i]);
for (j = 0; j < m; ++j)
{
if (pos[i][j] == 'm')
{
man[cntm] = info(i, j, cntm + 1);
++cntm;
}
}
}
for (i = 0; i < n; ++i)
{
for (j = 0; j < m; ++j)
{
if (pos[i][j] == 'H')
{
house[cnth] = info(i, j, cnth + cntm + 1);
++cnth;
}
}
} int S = 0, T = cntm + cnth + 1;
for (i = 0; i < cntm; ++i)
add(S, man[i].id, 1, 0); for (i = 0; i < cntm; ++i)
for (j = 0; j < cnth; ++j)
add(man[i].id, house[j].id, 1, man[i].cal(house[j])); for (i = 0; i < cnth; ++i)
add(house[i].id, T, 1, 0); MCMF(S, T);
printf("%d\n", mc);
}
return 0;
}

E - Minimum Cost

题目描述:有N个人要买K种东西,又有M种供应商,若最后这N个人均能买到对应数量的物品,则求最小花费;否则输出-1.

解题思路:由于有多种物品处理起来非常麻烦,但是可以发现每一种物品的采购情况对其他物品完全没有影响,又因为物品K只有50,因此可以对每一种物品求买该物品下的最消费用最大流,判断每一次得到的流量是否就是这种物品的总需求量,最后把所有物品的费用加起来即可。题目输入格式简直…………。

对于每一种物品若卖家拥有量大于0,则从源点连到卖家,流量为卖家拥有该物品的量,费用为0;然后若有买家对此物品有需求,则向它连一条边,流量为这个人需要的量,费用为这个物品的单位价钱,最后每一个买家连到汇点T一条边,流量为需要的量,费用为0,然后求K次费用流

代码:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <sstream>
#include <numeric>
#include <cstring>
#include <bitset>
#include <string>
#include <deque>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
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<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 110;
struct edge
{
int to, nxt;
int cap, cost;
edge() {}
edge(int To, int Nxt, int Cap, int Cost): to(To), nxt(Nxt), cap(Cap), cost(Cost) {}
};
edge E[N * N * 2];
int head[N], tot;
int dis[N], path[N], pre[N];
bitset<N>vis;
int mc, mf, totmc;
int need[N][N];
int Sp[N][N], Kind[N][N][N]; void reset()
{
CLR(head, -1);
tot = 0;
mc = mf = 0;
}
void init()
{
CLR(Sp, 0);
CLR(Kind, 0);
totmc = 0;
CLR(need, 0);
}
inline void add(int s, int t, int cap, int cost)
{
E[tot] = edge(t, head[s], cap, cost);
head[s] = tot++;
E[tot] = edge(s, head[t], 0, -cost);
head[t] = tot++;
}
int spfa(int s, int t)
{
CLR(dis, INF);
pre[s] = -1;
path[s] = -1;
dis[s] = 0;
vis.reset();
queue<int>Q;
Q.push(s);
vis[s] = true;
while (!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;
for (int i = head[u]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (dis[v] > dis[u] + E[i].cost && E[i].cap > 0)
{
dis[v] = dis[u] + E[i].cost;
pre[v] = u;
path[v] = i;
if (!vis[v])
{
vis[v] = true;
Q.push(v);
}
}
}
}
return dis[t] != INF;
}
void MFMC(int s, int t)
{
while (spfa(s, t))
{
int f = INF;
for (int i = t; i != s && ~i; i = pre[i])
f = min<int>(f, E[path[i]].cap);
for (int i = t; i != s && ~i; i = pre[i])
{
E[path[i]].cap -= f;
E[path[i] ^ 1].cap += f;
}
mf += f;
mc += dis[t] * f;
}
}
int main(void)
{
int n, m, k, q, i, j, a, b;
while (~scanf("%d%d%d", &n, &m, &k) && (n || m || k))
{
init();
int S = 0, T = n + m + 1;
for (i = 1; i <= n; ++i)
{
for (j = 1; j <= k; ++j)
scanf("%d", &need[i][j]);
}
for (i = 1; i <= m; ++i)
for (j = 1; j <= k; ++j)
scanf("%d", &Sp[i][j]);
for (q = 1; q <= k; ++q)
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j)
scanf("%d", &Kind[q][i][j]);
bool flag = true;
for (i = 1; i <= k; ++i)
{
reset();
for (a = 1; a <= m; ++a)
{
if (Sp[a][i])
{
add(S, a, Sp[a][i], 0);
for (b = 1; b <= n; ++b)
add(a, m + b, need[b][i], Kind[i][b][a]);
}
}
int sumneed = 0;
for (b = 1; b <= n; ++b)
{
add(m + b, T, need[b][i], 0);
sumneed += need[b][i];
}
MFMC(S, T);
//printf("%d %d\n",mc,mf);
if (mf == sumneed && flag)
totmc += mc;
else
{
flag = false;
break;
}
}
printf("%d\n", flag ? totmc : -1);
}
return 0;
}

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 <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <sstream>
#include <numeric>
#include <cstring>
#include <bitset>
#include <string>
#include <deque>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
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<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 110;
struct edge
{
int to, nxt;
int cap;
edge() {}
edge(int To, int Nxt, int Cap): to(To), nxt(Nxt), cap(Cap) {}
};
edge E[N * N * 2];
int head[N], tot;
int dis[N]; 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(dis, INF);
dis[s] = 0;
queue<int>Q;
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 (E[i].cap > 0 && dis[v] > dis[u] + 1)
{
dis[v] = dis[u] + 1;
if (v == t)
return 1;
Q.push(v);
}
}
}
return dis[t] != INF;
}
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 (E[i].cap > 0 && dis[v] == dis[s] + 1)
{
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)
dis[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, np, nc, m, a, b, c, i, id;
while (~scanf("%d%d%d%d", &n, &np, &nc, &m))
{
init();
int S = n, T = n + 1;
for (i = 0; i < m; ++i)
{
scanf(" (%d,%d)%d", &a, &b, &c);
add(a, b, c);
}
for (i = 0; i < np; ++i)
{
scanf(" (%d)%d", &id, &c);
add(S, id, c);
}
for (i = 0; i < nc; ++i)
{
scanf(" (%d)%d", &id, &c);
add(id, T, c);
}
printf("%d\n", dinic(S, T));
}
return 0;
}

G - Island Transport

题目描述:给定平面图中N个坐标点和M条边,求最西边的点到最东边的点的最大流量。

解题思路:正规解法其实应该是用什么转化为对偶图再跑最短路,由于没接触过这方面的知识,只好强行最大流了,Dinic低空飞过……

辣鸡代码(还是别看了,去学下正规解法吧):

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <bits/stdc++.h>
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<int, int> 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];
queue<int>Q; 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<int>(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 <stdio.h>
#include <bits/stdc++.h>
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<int, int> 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)
{
queue<int>Q;
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 <stdio.h>
#include <bits/stdc++.h>
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<int, int> 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)
{
queue<int>Q;
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 <stdio.h>
#include <bits/stdc++.h>
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<int, int> 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];
vector<pii>ans; 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)
{
queue<int>Q;
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 <stdio.h>
#include <bits/stdc++.h>
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<int, int> 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;
queue<int>q;
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;
}

L - Kakuro Extension

尼玛题目都看不懂,留坑好了


M - Escape

题目描述:有N个人,M个星球,他们要逃亡到这些星球上,但是每一个人只能去适宜的星球,星球本身也有容量限制,求最后可以逃走多少人

解题思路:若每一个人连到一个星球上则可能有$(100000+100000*10+10)*2$条边,可能会超时,但是由于星球数只有10个,因此一个人的选择状态最多有$2^{10}=1024$种,每一种状态里的人都是完全等价的,因此先把每一个人的选择状态储存下来,然后统计每一种选择状态下的人数,从源点连到某一种选择状态,流量为该状态的人数;从该种选择状态连到状态中适合的星球,流量为无穷大;再从星球连到汇点流量为星球的限制人数,最后求S到T的最大流量就是答案。

代码:

#include <stdio.h>
#include <bits/stdc++.h>
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<int, int> 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;
queue<int>q;
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 <stdio.h>
#include <bits/stdc++.h>
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<int, int> 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;
queue<int>q;
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 <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <sstream>
#include <numeric>
#include <cstring>
#include <bitset>
#include <string>
#include <deque>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
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<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 1010;
const int M = 100010;
struct edge
{
int to, nxt;
int cap;
int dx;
};
edge E[M << 1], dE[M];
int head[N], dhead[N], tot, dtot;
int d[N], dis[N];
bitset<N> inq; void init()
{
CLR(head, -1);
CLR(dhead, -1);
dtot = 0;
tot = 0;
CLR(dis, INF);
inq.reset();
}
inline void add(int s, int t, int cap)
{
E[tot].to = t;
E[tot].cap = cap;
E[tot].nxt = head[s];
head[s] = tot++; E[tot].to = s;
E[tot].cap = 0;
E[tot].nxt = head[t];
head[t] = tot++;
}
inline void addedge(int s, int t, int dx)
{
dE[dtot].to = t;
dE[dtot].dx = dx;
dE[dtot].nxt = dhead[s];
dhead[s] = dtot++;
}
void spfa(int s)
{
queue<int>Q;
Q.push(s);
inq[s] = 1;
dis[s] = 0;
while (!Q.empty())
{
int now = Q.front();
Q.pop();
inq[now] = 0;
for (int i = dhead[now]; ~i; i = dE[i].nxt)
{
int v = dE[i].to;
if (dis[v] > dis[now] + dE[i].dx)
{
dis[v] = dis[now] + dE[i].dx;
if (!inq[v])
{
inq[v] = 1;
Q.push(v);
}
}
}
}
}
int bfs(int s, int t)
{
queue<int>Q;
CLR(d, INF);
d[s] = 0;
Q.push(s);
while (!Q.empty())
{
int now = Q.front();
Q.pop();
for (int i = head[now]; ~i; i = E[i].nxt)
{
int v = E[i].to;
if (d[v] == INF && E[i].cap > 0)
{
d[v] = d[now] + 1;
if (v == t)
return 1;
Q.push(v);
}
}
}
return d[t] != INF;
}
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, a, b, dx, A, B, i, j;
scanf("%d", &tcase);
while (tcase--)
{
init();
scanf("%d%d", &n, &m);
for (i = 0; i < m; ++i)
{
scanf("%d%d%d", &a, &b, &dx);
if (a == b)
continue;
addedge(a, b, dx);
}
scanf("%d%d", &A, &B);
spfa(A);
for (a = 1; a <= n; ++a)
{
for (j = dhead[a]; ~j; j = dE[j].nxt)
{
b = dE[j].to;
if (dis[b] - dis[a] == dE[j].dx)
add(a, b, 1);
}
}
printf("%d\n", dinic(A, B));
}
return 0;
}

[kuangbin带你飞]专题十一 网络流个人题解(L题留坑)的更多相关文章

  1. [kuangbin带你飞]专题十一 网络流

            ID Origin Title   34 / 81 Problem A POJ 3436 ACM Computer Factory   92 / 195 Problem B POJ 3 ...

  2. Kuangbin 带你飞专题十一 网络流题解 及模版 及上下界网络流等问题

    首先是几份模版 最大流:虽然EK很慢但是优势就是短.求最小割的时候可以根据增广时的a数组来判断哪些边是割边.然而SAP的最大流版我只会套版,并不知道该如何找到这个割边.在尝试的时候发现了一些问题.所以 ...

  3. [kuangbin带你飞]专题一 简单搜索 题解报告

    又重头开始刷kuangbin,有些题用了和以前不一样的思路解决.全部题解如下 点击每道题的标题即可跳转至VJ题目页面. A-棋盘问题 棋子不能摆在相同行和相同列,所以我们可以依此枚举每一行,然后标记每 ...

  4. [kuangbin带你飞]专题1-23题目清单总结

    [kuangbin带你飞]专题1-23 专题一 简单搜索 POJ 1321 棋盘问题POJ 2251 Dungeon MasterPOJ 3278 Catch That CowPOJ 3279 Fli ...

  5. 【算法系列学习三】[kuangbin带你飞]专题二 搜索进阶 之 A-Eight 反向bfs打表和康拓展开

    [kuangbin带你飞]专题二 搜索进阶 之 A-Eight 这是一道经典的八数码问题.首先,简单介绍一下八数码问题: 八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的 ...

  6. [kuangbin带你飞]专题十 匹配问题

        A-L 二分匹配 M-O 二分图多重匹配 P-Q 二分图最大权匹配 R-S 一般图匹配带花树 模板请自己找     ID Origin Title   61 / 72 Problem A HD ...

  7. [kuangbin带你飞]专题十 匹配问题 一般图匹配

    过去做的都是二分图匹配 即 同一个集合里的点 互相不联通 但是如果延伸到一般图上去 求一个一般图的最大匹配 就要用带花树来解决 带花树模板 用来处理一个无向图上的最大匹配 看了一会还是不懂  抄了一遍 ...

  8. [kuangbin带你飞]专题十 匹配问题 二分匹配部分

    刚回到家 开了二分匹配专题 手握xyl模板 奋力写写写 终于写完了一群模板题 A hdu1045 对这个图进行 行列的重写 给每个位置赋予新的行列 使不能相互打到的位置 拥有不同的行与列 然后左行右列 ...

  9. [kuangbin带你飞]专题六 最小生成树

    学习最小生成树已经有一段时间了 做一些比较简单的题还算得心应手..花了三天的时间做完了kuangbin的专题 写一个题解出来记录一下(虽然几乎都是模板题) 做完的感想:有很多地方都要注意 n == 1 ...

随机推荐

  1. python 数据库操作 SQLite、MySQL 摘录

    转自: http://www.cnblogs.com/windlaughing/p/3157531.html 不管使用什么后台数据库,代码所遵循的过程都是一样的:连接 -> 创建游标 -> ...

  2. vs code vim

    很多初学者启动vim后,不知道怎么输入字符:按了半天字母,结果屏幕还是空的. vim和记事本或WORD不一样,不是一打开后就可以输入文字,此时它处于正常模式. vim一共有4个模式: 正常模式 (No ...

  3. pandas 代码

    def get_train_data(): df = pd.read_csv('data/train.csv', encoding='utf_8') # df1 = pd.read_csv('data ...

  4. final关键字,static关键字

    Final final的意思为最终,不可变.final是个修饰符,它可以用来修饰类,类的成员,以及局部变量.不能修饰构造方法. 注意: 被final修饰的类不能被继承但可以继承别的类 class Yy ...

  5. common-fileupload组件实现java文件上传和下载

    简介:文件上传和下载是java web中常见的操作,文件上传主要是将文件通过IO流传放到服务器的某一个特定的文件夹下,而文件下载则是与文件上传相反,将文件从服务器的特定的文件夹下的文件通过IO流下载到 ...

  6. java算法面试题:编写一个程序,将a.txt文件中的单词与b.txt文件中的单词交替合并到c.txt文件中,a.txt文件中的单词用回车符分隔,b.txt文件中用回车或空格进行分隔。

    package com.swift; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File ...

  7. TP5 发送邮件代码

    发送邮箱邮件方法 /** * 系统邮件发送函数 * @param string $tomail 接收邮件者邮箱 * @param string $name 接收邮件者名称 * @param strin ...

  8. python---列表(list)基本操作

    列表基本操作:增.删.查.改等其他操作 创建列表: list1 = ["a","b","c","d"] name_lis ...

  9. Hive将SQL转化为MapReduce的过程

    Hive将SQL转化为MapReduce的过程: Antlr定义SQL的语法规则,完成SQL词法,语法解析,将SQL转化为抽象语法树AST Tree 遍历AST Tree,抽象出查询的基本组成单元Qu ...

  10. linux学习(二) -- ubuntu下lnmp环境的配置

    亲测的教程,,希望能对大家提供些许帮助,转载请注明出处 ubuntu+nginx+mysql+php7 一.安装Nginx 1.首先添加nginx_signing.key(必须,否则出错) $ wge ...