网络流的题目最难的是构图。只要图构得好,丢到板子里跑一遍就可以了。由于网络流题目规模不会很大,所以最大流板子dinic基本都能满足要求。
最大流
CF653D - Delivery Bears
Description
二分牛搬运的货物总量(如果二分的是每只牛的量,最后结果会乘上牛总数n,使得误差放大n倍),然后求出流量网络中每条边能通过的最多的牛,跑一次最大流看看是否大于牛总数。
这题调精度调死我了。
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <string>
#include <deque>
#include <cmath>
#include <iomanip>
#include <cctype>
//#define endl '
'
#define IOS std::ios::sync_with_stdio(0)
#define FILE freopen("..//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen("..//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define md make_pair
#define pb push_back
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
inline ll qmul(ll a, ll b, ll m) {
ll res = 0;
while(b) {
if(b & 1) res = (res + a) % m;
a = (a << 1) % m;
b = b >> 1;
}
return res;
}
inline ll qpow(ll a, ll b, ll m) {
ll res = 1;
while(b) {
if(b & 1) res = (res * a) % m;
a = (a * a) % m;
b = b >> 1;
}
return res;
}
inline ll inv(ll x, ll q) {
return qpow(x, q - 2, q);
}
/*-----------------------------------------------------------------*/
#define INF 0x3f3f3f3f
using namespace std;
const int N = 500;
const int M = 3e3;
int dis[N];
struct edge{
int np, ne;
double f;
ll num;
};
edge ed[M];
int head[N];
int si = 2;
void add(int u, int v, double f) {
ed[si].f = f;
ed[si].ne = head[u];
ed[si].np = v;
head[u] = si;
si++;
}
bool bfs(int s, int tar) {
memset(dis, 0, sizeof dis);
queue<int> q;
q.push(s);
dis[s] = 1;
while(!q.empty()) {
int cur = q.front();
q.pop();
for(int i = head[cur]; i; i = ed[i].ne) {
int np = ed[i].np;
if(!dis[np] && ed[i].num) {
dis[np] = dis[cur] + 1;
q.push(np);
}
}
}
return dis[tar];
}
int dfs(int p, int flo, int tar) {
if(p == tar) return flo;
int delta = flo;
for(int i = head[p]; i; i = ed[i].ne) {
int np = ed[i].np;
if(dis[p] + 1 == dis[np] && (ed[i].num)) {
int d = dfs(np, min((ll)delta, ed[i].num), tar);
ed[i].num -= d; ed[i^1].num += d;
delta -= d;
}
if(delta == 0) break;
}
return flo - delta;
}
bool dini(int n, int tot) {
int ans = 0;
while(bfs(1, n)) ans += dfs(1, INF,n);
return ans >= tot;
}
const double eps = 1e-8;
bool check(double x, int n, int tot) {
for(int i = 2; i < si; i++) {
ed[i].num = ed[i].f / x * tot;
}
return dini(n, tot);
}
int main() {
IOS;
int n, m, x;
cin >> n >> m >> x;
for(int i = 0; i < m; i++) {
int u, v;
double f;
cin >> u >> v >> f;
add(v, u, 0);
add(u, v, f);
}
double l = 0, r = INF;
while(r - l >= eps) {
double mid = (l + r) / 2;
if(check(mid,n, x)) l = mid;
else r = mid;
}
cout << fixed << setprecision(8) << l << endl;
}
CF546E - Soldier and Traveling
每个城市一个点,然后拆点。留在城中,就连条边连向自己;去别的城市,就连别的城市,容量为INF。源点连每个城市,容量为士兵数;拆的点连汇点,容量为每个城市目标人数。满流代表有解。
至于怎么看每个城市流出多少人,看反向边的流量即可。
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <string>
#include <deque>
#include <cmath>
#include <iomanip>
#include <cctype>
#define endl '
'
#define IOS std::ios::sync_with_stdio(0)
#define FILE freopen("..//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen("..//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define md make_pair
#define pb push_back
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
inline ll qmul(ll a, ll b, ll m) {
ll res = 0;
while(b) {
if(b & 1) res = (res + a) % m;
a = (a << 1) % m;
b = b >> 1;
}
return res;
}
inline ll qpow(ll a, ll b, ll m) {
ll res = 1;
while(b) {
if(b & 1) res = (res * a) % m;
a = (a * a) % m;
b = b >> 1;
}
return res;
}
inline ll inv(ll x, ll q) {
return qpow(x, q - 2, q);
}
/*-----------------------------------------------------------------*/
#define INF 0x3f3f3f3
using namespace std;
const int N = 300;
const int M = 1e5 + 10;
int dis[N];
struct edge{
int np, ne;
ll f;
};
edge ed[M];
int head[N];
int si = 2;
void add(int u, int v, ll f) {
ed[si] = edge{v, head[u], f};
head[u] = si;
si++;
ed[si] = edge{u, head[v], 0};
head[v] = si;
si++;
}
bool bfs(int s, int tar) {
memset(dis, 0, sizeof dis);
queue<int> q;
q.push(s);
dis[s] = 1;
while(!q.empty()) {
int cur = q.front();
q.pop();
for(int i = head[cur]; i; i = ed[i].ne) {
int np = ed[i].np;
if(!dis[np] && ed[i].f) {
dis[np] = dis[cur] + 1;
q.push(np);
}
}
}
return dis[tar];
}
ll dfs(int p, ll flo, int tar) {
if(p == tar) return flo;
ll delta = flo;
for(int i = head[p]; i; i = ed[i].ne) {
int np = ed[i].np;
if(dis[p] + 1 == dis[np] && (ed[i].f)) {
ll d = dfs(np, min(delta, ed[i].f), tar);
ed[i].f -= d; ed[i^1].f += d;
delta -= d;
}
if(delta == 0) break;
}
return flo - delta;
}
ll dini(int s, int t) {
ll ans = 0;
while(bfs(s, t)) {
ans += dfs(s, INF,t);
}
return ans;
}
ll a[N];
ll b[N];
int ans[N][N];
void getans(int s, int n) {
for(int e = head[s]; e; e = ed[e].ne) {
int cur = ed[e].np;
for(int ei = head[cur]; ei; ei = ed[ei].ne) {
ans[cur][ed[ei].np - n] = ed[ei^1].f;
}
}
}
int main() {
IOS;
int n, m;
cin >> n >> m;
ll ta = 0, tb = 0;
for(int i = 1; i <= n; i++) {cin >> a[i]; ta += a[i];}
for(int i = 1; i <= n; i++) {cin >> b[i]; tb += b[i];}
if(ta != tb) cout << "NO" << endl;
else {
int s= 0, t = 2 * n + 1;
for(int i= 1; i <= n; i++) {
add(s, i, a[i]);
add(i + n, t, b[i]);
add(i, i + n, INF);
}
while(m--) {
int u, v;
cin >> u >> v;
add(u, v + n, INF);
add(v, u + n, INF);
}
ll res = dini(s, t);
if(res == ta) {
cout << "YES" << endl;
getans(s, n);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
cout << ans[i][j] << " ";
}
cout << endl;
}
} else cout << "NO" << endl;
}
}
HDU3605 - Escape
Description
很简单的最大流题。虽然n有1e5那么大,但由于m非常小,小于等于10。一共有(2^m)种状态,所以只要统计每种状态有多少人,利用状态建图就可以了。
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <string>
#include <deque>
#include <cmath>
#include <iomanip>
#include <cctype>
#define endl '
'
#define IOS std::ios::sync_with_stdio(0);
#define FILE freopen("..//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen("..//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define md make_pair
#define pb push_back
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
inline ll qmul(ll a, ll b, ll m) {
ll res = 0;
while(b) {
if(b & 1) res = (res + a) % m;
a = (a << 1) % m;
b = b >> 1;
}
return res;
}
inline ll qpow(ll a, ll b, ll m) {
ll res = 1;
while(b) {
if(b & 1) res = (res * a) % m;
a = (a * a) % m;
b = b >> 1;
}
return res;
}
inline ll inv(ll x, ll q) {
return qpow(x, q - 2, q);
}
/*-----------------------------------------------------------------*/
#define INF 0x3f3f3f3f
using namespace std;
const int N = 3e3 + 10;
const int M = 2e5 + 10;
const double eps = 1e5;
struct edge {
int ne, np, f;
};
edge ed[M];
int head[N];
int cur[N];
int si = 2;
int dis[N];
int arr[N];
ll cost[N];
void init() {
si = 2;
memset(head, 0, sizeof head);
memset(cur, 0, sizeof cur);
}
void add(int u, int v, int f) {
ed[si] = edge{head[u], v, f};
head[u] = si;
cur[u] = head[u];
si++;
ed[si] = edge{head[v], u, 0};
head[v] = si;
cur[v] = head[v];
si++;
}
bool bfs(int s, int t) {
memset(dis, 0, sizeof dis);
queue<int> q;
q.push(s);
dis[s] = 1;
while(!q.empty()) {
int cur = q.front();
q.pop();
for(int i = head[cur]; i; i = ed[i].ne) {
int nt = ed[i].np;
if(dis[nt] || (!ed[i].f)) continue;
dis[nt] = dis[cur] + 1;
q.push(nt);
}
}
return dis[t];
}
int dfs(int p, int t, int flo) {
if(p == t) return flo;
int delta = flo;
for(int &i = cur[p]; i; i = ed[i].ne) {
int nt = ed[i].np;
if(dis[nt] == dis[p] + 1 && ed[i].f) {
int d = dfs(nt, t, min(delta, ed[i].f));
delta -= d;
ed[i].f -= d; ed[i^1].f += d;
if(delta == 0) break;
}
}
cur[p] = head[p];
return flo - delta;
}
ll dini(int s, int t) {
ll ans = 0;
while(bfs(s, t)) {
ans += dfs(s, t, INF);
}
return ans;
}
int cnt[N];
int limilt[N];
int main() {
IOS;
int n, m;
while(cin >> n >> m) {
init();
memset(cnt, 0, sizeof cnt);
for(int i = 1; i <= n; i++) {
int pos = 0;
for(int j = 0; j < m; j++) {
int y;
cin >> y;
if(y) pos += (1 << j);
}
cnt[pos]++;
}
for(int i = 1; i <= m; i++) cin >> limilt[i];
int mxp = 1024;
int s = 0, t = mxp + 2 * m + 1;
for(int i = 0; i < (1 << 10); i++) {
if(!cnt[i]) continue;
add(s, i + 1, cnt[i]);
for(int j = 1; j <= m; j++) {
if((i >> (j - 1)) & 1) add(i + 1, mxp + j, INF);
}
}
for(int i = 1; i <= m; i++) {
add(mxp + i, t, limilt[i]);
}
cout << ((dini(s, t) == n) ? "YES" : "NO") << endl;
}
}
HDU4292 - Food
经典的点限制拆点。
每个人左边连喜欢吃的食物,右边连喜欢喝的饮料。每个人拆点,中间连容量为1的边,代表一个人只能选一个食物和饮料。求最大流。
就是这样(图来自《挑战程序设计竞赛》)
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <string>
#include <deque>
#include <cmath>
#include <iomanip>
#include <cctype>
//#define endl '
'
#define IOS std::ios::sync_with_stdio(0);
#define FILE freopen("..//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen("..//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define md make_pair
#define pb push_back
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
inline ll qmul(ll a, ll b, ll m) {
ll res = 0;
while(b) {
if(b & 1) res = (res + a) % m;
a = (a << 1) % m;
b = b >> 1;
}
return res;
}
inline ll qpow(ll a, ll b, ll m) {
ll res = 1;
while(b) {
if(b & 1) res = (res * a) % m;
a = (a * a) % m;
b = b >> 1;
}
return res;
}
inline ll inv(ll x, ll q) {
return qpow(x, q - 2, q);
}
/*-----------------------------------------------------------------*/
#define INF 0x3f3f3f3f
using namespace std;
const int N = 1000 + 10;
const int M =5e5 + 10;
const double eps = 1e5;
struct edge {
int ne, np, f;
};
edge ed[M];
int head[N];
int si = 2;
int dis[N];
void init() {
si = 2;
memset(head, 0, sizeof head);
}
void add(int u, int v, int f) {
ed[si] = edge{head[u], v, f};
head[u] = si;
si++;
ed[si] = edge{head[v], u, 0};
head[v] = si;
si++;
}
bool bfs(int s, int t) {
memset(dis, 0, sizeof dis);
queue<int> q;
q.push(s);
dis[s] = 1;
while(!q.empty()) {
int cur = q.front();
q.pop();
for(int i = head[cur]; i; i = ed[i].ne) {
int nt = ed[i].np;
if(dis[nt] || (!ed[i].f)) continue;
dis[nt] = dis[cur] + 1;
q.push(nt);
}
}
return dis[t];
}
int dfs(int p, int t, int flo) {
if(p == t) return flo;
int delta = flo;
for(int i = head[p]; i; i = ed[i].ne) {
int nt = ed[i].np;
if(dis[nt] == dis[p] + 1 && ed[i].f) {
int d = dfs(nt, t, min(delta, ed[i].f));
delta -= d;
ed[i].f -= d; ed[i^1].f += d;
if(delta == 0) break;
}
}
return flo - delta;
}
int dini(int s, int t) {
int ans = 0;
while(bfs(s, t)) {
ans += dfs(s, t, INF);
}
return ans;
}
int main() {
IOS;
int n, f, d;
while(cin >> n >> f >> d) {
init();
int s = 0, t = f + 2 * n + d + 2;
for(int i = 1; i <= f; i++) {
int v;
cin >> v;
add(s, i, v);
}
for(int i = 1; i <= d; i++) {
int v;
cin >> v;
add(f + 2 * n + i, t, v);
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= f; j++) {
char ch;
cin >> ch;
if(ch == 'Y') add(j, f + i, 1);
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= d; j++) {
char ch;
cin >> ch;
if(ch == 'Y') add(n + f + i, f + 2 * n + j, 1);
}
}
for(int i = 1; i <= n; i++) add(f + i, f + i + n, 1);
cout << dini(s, t) << endl;
}
}