Control-2012 ACM/ICPC Asia Regional Chengdu Online(2020ICPC成都站网络赛)(网络流-最小割)
judge: HDUOJ4289
题意
现有 n n n 个点, m m m 条边,每个点有一定的权值。
请选出总权值之和最小的点集,使得从 S S S 到 T T T 的任何一个路径都至少包含一个点集中的点。
你只需要输出点集的总权值。题目保证从 S S S 到 T T T 一定有至少一条路径。
题解
拆点,把点 u u u 拆成 u u u 和 n + u n+u n+u。一个作为“入点”,只负责接受别的点的来访,另一个作为“出点”,负责访问别的点。
设点u的权值为 v a l [ u ] val[u] val[u],“入点”和“出点”之间连一条容量为 v a l [ u ] val[u] val[u] 的边,其余的边的容量设置为无穷大。
当我们在这样的图上访问一条路径时,拆出的点之间的边的容量就会限制路径的最大流量为路径上的点的最小权值。对这样的图求 S S S 到 T T T 的最大流就得出了覆盖 S S S 到 T T T 的所有路径的最小权值的和,也即是答案。
代码
#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <random>
#include <ctime>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 405;
const int maxm = 40005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;
inline int read() {
int x(0), f(1); char ch(getchar());
while (ch<'0' || ch>'9') {
if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') {
x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int n, m, S, T;
int val[maxn];
struct edge {
int to, cap, rev; //用于表示边的结构体(终点,容量,反向边)
edge() {
}
edge(int t, int cap, int rev) :to(t), cap(cap), rev(rev) {
}
};
vector <edge> mf_G[maxm]; //图的邻接表表示
int mf_dis[maxm]; //顶点到源点的距离标号
int mf_cur[maxm]; //当前弧,在其之前的边已经没有用了
void mf_init(){
for(int i = 0; i < maxn; i++) mf_G[i].clear();
}
void mf_addedge(int from, int to, int cap) {
mf_G[from].push_back(edge(to, cap, mf_G[to].size()));
mf_G[to].push_back(edge(from, 0, mf_G[from].size() - 1));
}
void mf_bfs(int s) {
//通过mf_bfs计算从源点出发的距离标号
memset(mf_dis, -1, sizeof(mf_dis));
queue <int> q;
mf_dis[s] = 0;
q.push(s);
while (!q.empty()) {
int v = q.front(); q.pop();
for (int i = 0; i < mf_G[v].size(); i++) {
edge &e = mf_G[v][i];
if (e.cap > 0 && mf_dis[e.to] < 0) {
mf_dis[e.to] = mf_dis[v] + 1;
q.push(e.to);
}
}
}
}
int mf_dfs(int v, int t, int f) {
//通过mf_dfs寻找增广路
if (v == t) return f;
for (int &i = mf_cur[v]; i < mf_G[v].size(); i++) {
edge &e = mf_G[v][i];
if (e.cap > 0 && mf_dis[v] < mf_dis[e.to]) {
int d = mf_dfs(e.to, t, min(f, e.cap));
if (d > 0) {
e.cap -= d;
mf_G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
int maxflow(int s, int t) {
int flow = 0;
for (;;) {
mf_bfs(s); //计算层次图
if (mf_dis[t] < 0) return flow; //找不到s-t路径
memset(mf_cur, 0, sizeof(mf_cur)); //初始化当前弧
int f;
while ((f = mf_dfs(s, t, inf)) > 0) {
//更新最大流
flow += f;
}
}
return flow;
}
void init() {
mf_init();
}
void sol() {
init();
_rep(i, 1, n) val[i] = read();
_rep(i, 1, n) mf_addedge(i, n + i, val[i]);
_for(i, m) {
int u = read(), v = read();
mf_addedge(n + u, v, inf);
mf_addedge(n + v, u, inf);
}
printf("%lld\n", maxflow(S, n + T));
}
int main() {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
debug = 1;
#endif
time_t beg, end;
if(debug) beg = clock();
while(cin>>n>>m) {
S = read(), T = read();
sol();
}
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
return 0;
}
Minimum Cost(POJ Monthly–2005.07.31, Wang Yijie)(网络流-最小费用最大流)
judge: POJ2516
题意
现有 n n n 个店家, m m m 个供应商, k k k 种货物。
每个供应商给每个店家供应每种货物有不同的花费单价。每个店家对每种货物有不同的需求数量,每个供应商对每种货物有不同的供应数量。
若有满足所有店家的需求的方案的话,请输出最小的总花费。否则输出-1
。
题解
对源点和汇点相连的限制流量,对店家和供应商之间的边限制费用,跑最小费用最大流即可。
#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <ctime>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 105;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;
inline int read() {
int x(0), f(1); char ch(getchar());
while (ch<'0' || ch>'9') {
if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') {
x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int n, m, S, T, k;
int nk[maxn][maxn], mk[maxn][maxn], nm[maxn][maxn];
struct edge {
int u, v, cap, flow, cost, next;
edge() {
}
edge(int u, int v, int cap, int flow, int cost, int next) :u(u), v(v), cap(cap), flow(flow), cost(cost), next(next) {
}
}edges[maxm];
int head[maxm], dis[maxn], vis[maxn], Min[maxn], cnt;
int pre[maxn];
void init() {
cnt = 0;
memset(head, -1, sizeof(head));
memset(dis, 0x3f, sizeof(dis));
}
void adde(int u, int v, int cap, int cost) {
edges[cnt] = edge(u, v, cap, 0, cost, head[u]);
head[u] = cnt++;
edges[cnt] = edge(v, u, 0, 0, -cost, head[v]);
head[v] = cnt++;
}
bool SPFA(int s, int t) {
memset(vis, 0, sizeof(vis));
memset(dis, 0x3f, sizeof(dis));
memset(pre, -1, sizeof(pre));
dis[s] = 0;
vis[s] = 1;
Min[s] = inf;
queue<int> Q;
Q.push(s);
while (Q.size()) {
int u = Q.front();
Q.pop();
vis[u] = 0;
for (int i = head[u]; ~i; i = edges[i].next) {
edge &e = edges[i];
if (e.cap > e.flow && dis[e.v] > dis[u] + e.cost) {
dis[e.v] = dis[u] + e.cost;
pre[e.v] = i;
Min[e.v] = min(e.cap - e.flow, Min[u]);
if (!vis[e.v]) {
Q.push(e.v);
vis[e.v] = 1;
}
}
}
}
return pre[t] != -1;
}
int Mincost(int s, int t, int &cost) {
int flow = 0;
cost = 0;
while (SPFA(s, t)) {
int _min = Min[t];
for (int i = pre[t]; ~i; i = pre[edges[i ^ 1].v]) {
edges[i].flow += _min;
edges[i ^ 1].flow -= _min;
cost += edges[i].cost * _min;
}
flow += _min;
}
return flow;
}
void sol() {
_for(i, n) _for(j, k) nk[i][j] = read();
_for(i, m) _for(j, k) mk[i][j] = read();
int sc = 0, fla = 1;
_for(i, k) {
_for(j, n) _for(l, m) nm[j][l] = read();
init();
_for(j, n) adde(0, j + 1, nk[j][i], 0);
_for(j, m) adde(j + 1 + n, n + m + 1, mk[j][i], 0);
_for(j, n) {
_for(l, m) {
adde(j + 1, n + 1 + l, inf, nm[j][l]);
}
}
int cost = 0;
int mf = Mincost(0, n + m + 1, cost);
int sum = 0;
_for(j, n) sum += nk[j][i];
if(sum != mf) fla = 0;
sc += cost;
}
printf("%d\n", fla ? sc : -1);
}
int main() {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
debug = 1;
#endif
time_t beg, end;
if(debug) beg = clock();
while(cin>>n>>m>>k, n || m || k) {
sol();
}
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
return 0;
}
A Plug for UNIX(East Central North America 1999)(网络流-最大流)
judge: poj1087
题意
现有 n n n 个插座, m m m 个设备,每个设备对应一种插头(设备的插头不一定能插到 n n n 个插座中), q q q 个转换器,每个转换器有一个插座和一个插头。
求出最小的不能查到插座上的设备的数量。
题解
先按常规方式建图,把源点和每一个现有的插座连一条容量为 1 1 1 的边,每个插座和能插到上面的设备之间连一条容量为 1 1 1 的边,每个设备和汇点连一条容量为 1 1 1 的边。
至于转换器的处理,可以从(和转换器的插头对应的的插座)向(转换器的插座类型的插座)连一条容量为 I N F INF INF 的边。
这里给出一组测试数据
输入
4
A
B
C
D
4
a D
b D
c D
d D
3
D A
D B
D C
输出
0
代码
#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <ctime>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 1005;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;
inline int read() {
int x(0), f(1); char ch(getchar());
while (ch<'0' || ch>'9') {
if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') {
x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
struct edge {
int to, cap, rev; //用于表示边的结构体(终点,容量,反向边)
edge() {
}
edge(int t, int cap, int rev) :to(t), cap(cap), rev(rev) {
}
};
vector <edge> mf_G[maxm]; //图的邻接表表示
int mf_dis[maxm]; //顶点到源点的距离标号
int mf_cur[maxm]; //当前弧,在其之前的边已经没有用了
void mf_init(){
for(int i = 0; i < maxn; i++) mf_G[i].clear();
}
void mf_addedge(int from, int to, int cap) {
mf_G[from].push_back(edge(to, cap, mf_G[to].size()));
mf_G[to].push_back(edge(from, 0, mf_G[from].size() - 1));
}
void mf_bfs(int s) {
//通过mf_bfs计算从源点出发的距离标号
memset(mf_dis, -1, sizeof(mf_dis));
queue <int> q;
mf_dis[s] = 0;
q.push(s);
while (!q.empty()) {
int v = q.front(); q.pop();
for (int i = 0; i < mf_G[v].size(); i++) {
edge &e = mf_G[v][i];
if (e.cap > 0 && mf_dis[e.to] < 0) {
mf_dis[e.to] = mf_dis[v] + 1;
q.push(e.to);
}
}
}
}
int mf_dfs(int v, int t, int f) {
//通过mf_dfs寻找增广路
if (v == t) return f;
for (int &i = mf_cur[v]; i < mf_G[v].size(); i++) {
edge &e = mf_G[v][i];
if (e.cap > 0 && mf_dis[v] < mf_dis[e.to]) {
int d = mf_dfs(e.to, t, min(f, e.cap));
if (d > 0) {
e.cap -= d;
mf_G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
int maxflow(int s, int t) {
int flow = 0;
for (;;) {
mf_bfs(s); //计算层次图
if (mf_dis[t] < 0) return flow; //找不到s-t路径
memset(mf_cur, 0, sizeof(mf_cur)); //初始化当前弧
int f;
while ((f = mf_dfs(s, t, inf)) > 0) {
//更新最大流
flow += f;
}
}
return flow;
}
int n, m, q, cnt;
map<string, int> mp;
void init() {
mp.clear();
cnt = 0;
mf_init();
}
void sol() {
init();
n = read();
_for(i, n) {
string s;
cin >> s;
mp[s] = ++cnt;
mf_addedge(0, mp[s], 1);
}
m = read();
string u, v;
_for(i, m) {
cin >> u >> v;
if(!mp.count(u)) mp[u] = ++cnt;
if(!mp.count(v)) mp[v] = ++cnt;
mf_addedge(mp[v], mp[u], 1);
mf_addedge(mp[u], n + m + 1, 1);
}
q = read();
_for(i, q) {
cin >> u >> v;
if(!mp.count(u)) mp[u] = ++cnt;
if(!mp.count(v)) mp[v] = ++cnt;
mf_addedge(mp[v], mp[u], inf);
}
cout << m - maxflow(0, n + m + 1) << "\n";
}
int main() {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
debug = 1;
#endif
time_t beg, end;
if(debug) beg = clock();
sol();
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
return 0;
}
SPF(Greater New York 2000)(强连通-求割点)
judge: poj1523
题意
给你一个图,有若干个点,每个点有一个数值不超过1000的编号,找出图中的所有割点,并输出删除割点后图中存在几个连通块。
题解
详情见2020ICPC·小米 网络选拔赛第一场部分题解 D-Router Mesh,两道题的解法一模一样。
此题需要注意的是点的编号不一定是连续的。
代码
#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <ctime>
#include <stack>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 3005;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;
inline int read() {
int x(0), f(1); char ch(getchar());
while (ch<'0' || ch>'9') {
if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') {
x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int n = 0;
vector<int> G[maxn];
int num[maxn];
int scccnt; //强连通分量的数量
int sccno[maxn]; //每个点所在的强连通分量的编号,编号从1开始递增
int dfn[maxn]; //深度优先搜索遍历时结点 u 被搜索的次序
int low[maxn]; //u或u的子树能够追溯到的最早的栈中节点的次序号
int tclock; //用于递增dfn
stack<int> q;
void init() {
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(sccno, 0, sizeof(sccno));
_for(i, maxn) G[i].clear(), num[i] = 0;
scccnt = tclock = 0;
}
void tarjin(int u) {
++num[u];
dfn[u] = low[u] = ++tclock;
q.push(u);
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if (!dfn[v]) {
tarjin(v);
low[u] = min(low[u], low[v]);
num[u] += (dfn[u] <= low[v]);
}
else if (!sccno[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
scccnt++;
int v = -1;
while (v != u) {
v = q.top();
q.pop();
sccno[v] = scccnt;
}
}
}
vector<int> pos;
int main() {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
debug = 1;
#endif
int u, v, tt = 1;
while(u = read(), u) {
pos.clear();
printf("Network #%d\n", tt++);
init();
v = read();
pos.push_back(u);
pos.push_back(v);
G[u].push_back(v);
G[v].push_back(u);
while(u = read(), u) {
v = read();
pos.push_back(u);
pos.push_back(v);
G[u].push_back(v);
G[v].push_back(u);
}
sort(pos.begin(), pos.end());
pos.erase(unique(pos.begin(), pos.end()), pos.end());
_for(i, pos.size()) {
int v = pos[i];
if(!dfn[v]) {
tarjin(v);
--num[v];
}
}
int f = 0;
_for(i, pos.size()) {
int v = pos[i];
if(num[v] != 1) {
printf(" SPF node %d leaves %d subnets\n", v, scccnt - 1 + num[v]);
f = 1;
}
}
if(!f) printf(" No SPF nodes\n");
printf("\n");
}
return 0;
}