文章目录

  • 概 念
    • 网络流图
    • 可行流
    • 可改进路(增广路)
    • 残留网络
    • 基本理论
    • 求最大流思路
    • EK算法
    • dinic
    • 最小费用最大流
    • EK+spfa
    • dinic + spfa
  • 例 题
    • poj Power Network
    • [网络流24题]最小路径覆盖问题
    • P2765 魔术球问题
    • [网络流24题]最长递增子序列问题
    • [CQOI2015]网络吞吐量
    • [SCOI2007]蜥蜴
    • PIGS
    • [网络流24题]飞行员配对方案问题
    • [网络流24题]圆桌问题
    • Fox And Dinner
    • Optimal Milking
    • Dining
    • [网络流24题]星际转移问题
    • [网络流24题]方格取数问题
      • 最小割
      • 二分图点权最大独立集
    • 最大获利
      • 最大权图
    • Magic Slab
    • 网络流24题]餐巾计划问题
    • E. Goods transportation

概 念

网络流图

若有向图G=(V,E)G=(V,E)G=(V,E)满足:

  • 有且仅有一个顶点SSS,它的入读为000,即d−(S)=0d-(S) = 0d−(S)=0,这个顶点S便称为源点。
  • 有且仅有一个顶点TTT,它的出度为000,即d+(T)=0d+(T) = 0d+(T)=0,这个顶点T便称为汇点。
  • 每一条弧都有非负数,叫做这个边的容量。边(vi,vj)(vi,vj)(vi,vj)的容量用CijCijCij表示
    则称之为网络流图,记为G=(V,E,C)G = (V,E,C)G=(V,E,C)

.......................................................................................................................................................................................

可行流

用fffi,j表示从iii到jjj的流量,满足:

  • 没一条弧(i,j)(i,j)(i,j)有fffi,j <=C<= C<=Ci,j
  • 流量平衡:除源点和汇点以外的所有点vivivi,有 ΣΣΣjfffi,j =Σ= Σ=Σkfffj,k
  • 对于源点SSS和汇点TTT有,ΣifΣifΣifS,i =Σjf= Σjf=Σjfj,T =v(f)= v(f)=v(f)

.......................................................................................................................................................................................

可改进路(增广路)

找到一条路径,使原来S−>TS->TS−>T的流量增加。

残留网络

由残留的容量(边的容量减去可行流)以及源点汇点构成的网络。

在容量网络 G(V,E)G(V, E)G(V,E) 中, 设 E′⊆EE'⊆EE′⊆E, 如果在 GGG 的基图中删去 E′E'E′ 后不再连通, 则称 E′E'E′ 是 GGG 的割。割将 GGG 的顶点集 VVV 划分成两个子集 SSS 和 T=V−ST = V - ST=V−S。将割记为(S,T)(S, T)(S,T)。

基本理论

  • 对于S−TS-TS−T一个可行流f,ff,ff,f一定小于割的容量。
  • 可行流f是最大流的充分必要条件是:残留网络中不存在可改进流量。
  • 最大流最小割定理 - 最大流等于最小割

求最大流思路

FF方法

  1. 初始化网络中各个边的容量,c<u,v>c<u,v>c<u,v>为该边的容量,c<u,v>c<u,v>c<u,v>为初始化0。
  2. 在残留网络中找到一条sss到ttt的增广路。能找到到3,不能到5
  3. 在增广路中找到容量最小的边记为xxx,加到最大流中。
  4. 在增广路中所有边的流量减去xxx,反向边加上xxx,构成新的残留网络,转到2
  5. 得到最大流。

.......................................................................................................................................................................................

EK算法

int EK(int s,int t){int flow = 0 ;while(1){memset(a,0,sizeof a);queue<int> q;q.push(s);a[s] = inf;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(!a[y] && w[i] > 0){a[y] = min(a[u],w[i]);pre[y] = i;q.push(y);}}if(a[t])break;}if(!a[t])break;for(int i = t;i!=s;i=e[pre[i]^1]){w[pre[i]] -= a[t],w[pre[i]^1] += a[t];}flow += a[t];}return flow;
}

dinic

bool bfs(int s,int t){memset(st,0,sizeof st);memset(d,inf,sizeof d);queue<int> q;q.push(s);st[s] = true;d[s] = 0;while(!q.empty()){int u = q.front();q.pop();st[u] = false;for(int i = h[u];~i;i=ne[i]){int y = e[i];if(w[i] > 0 && d[y] > d[u] + 1){d[y] = d[u] + 1;if(!st[y])q.push(y); }}}return d[t] != inf;
}
int dfs(int s,int mw,int t){if(s == t)return mw;for(int &i = cur[s];~i;i=ne[i]){int y = e[i];if(w[i] <= 0 || d[y] != d[s] + 1)continue;int cw = dfs(y,min(mw,w[i]),t);if(cw <= 0)continue;w[i] -= cw;w[i^1] += cw;return cw; }return 0;
}
int dinic(int s,int t){int flow = 0;while(bfs(s,t)){memcpy(cur,h,sizeof h);while(int d = dfs(s,inf,t)) flow += d;}return flow;
}

最小费用最大流

保证S-T流量最大的情况下,所需费用最小。

EK+spfa

bool spfa(int s,int t){for(int i = 1;i<N;i++)dist[i] = inf,d[i] = 0,st[i] = 0;dist[s] = 0,d[s] = inf,st[s] = true;queue<int> q;q.push(s);while(!q.empty()){int u = q.front();q.pop();st[u] = false;for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] && dist[y] > dist[u] + w[i]){dist[y] = dist[u] + w[i];d[y] = min(d[u],f[i]);pre[y] = i;  if(!st[y]){q.push(y);st[y] = true; }}}     }return  dist[t] != inf;
}
int EK(int s,int t){int cost = 0,flow = 0;while(spfa(s,t)){for(int i = t;i!=s;i=e[pre[i]^1]){f[pre[i]] -= d[t];f[pre[i]^1] += d[t];}//根据条件改变cost += dist[t]*d[t]; flow += d[t];}return cost,flow;
}

dinic + spfa

int cur[M];
long long dist[M];
bool st[M];
long long cost;
bool spfa(int s,int t){for(int i = 0;i<N;i++)dist[i] = inf64,st[i] = 0;dist[s] = 0;st[s] = true;queue<int> q;q.push(s);while(!q.empty()){int u = q.front();q.pop();st[u] = false;for(int i = h[u];~i;i=ne[i]){int y = e[i];if(dist[y] > dist[u] + w[i] && f[i]){dist[y] = dist[u] + w[i];if(!st[y])st[y] = true,q.push(y);}}//cout <<u << endl;}return dist[t] != inf64;
}
long long dfs(int u,int t,long long mw){if(u == t)return mw;st[u] = true;for(int &i = cur[u];~i;i=ne[i]){int y = e[i];if(f[i] <= 0 || dist[y] != dist[u] + w[i] || st[y])continue;long long cw = dfs(y,t,min(f[i],mw));if(cw <= 0)continue;f[i] -= cw;f[i^1] += cw;cost += (long long)w[i] * cw;return cw;}st[u] = false;return 0;
}
long long dinic(int s,int t){while(spfa(s,t)){memcpy(cur,h,sizeof cur);while(dfs(s,t,inf64));}return cost;
}

例 题

poj Power Network

题目链接

EK超时,dinic500ms

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#include<map>
//526526
using namespace std;
const int N=310,M = 3e4+10,inf = 0x3f3f3f3f;
int h[N],ne[M],e[M],f[M],idx;
int d[N];
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int cur[N];
bool st[N];
int n,n1,n2,m;
int a[N],pre[N];
int S = 101,T=102;
/*
int EK(){int flow = 0;while(1){memset(a,0,sizeof a);queue<int> q;q.push(S);a[S] = inf;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(!a[y] && f[i] > 0){a[y] = min(a[u],f[i]);pre[y] = i;q.push(y);}}if(a[T])break;}if(!a[T])break;for(int i = T;i!=S;i=e[pre[i]^1]){f[pre[i]] -= a[T];f[pre[i]^1] += a[T];}flow += a[T];}return flow;
}
*/
bool dfs(int s,int t){memset(st,0,sizeof st);memset(d,inf,sizeof d);queue<int> q;q.push(s);st[s] = true;d[s] = 0;while(!q.empty()){int u = q.front();q.pop();st[u] = false;for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] > 0 && d[y] > d[u] + 1){d[y] = d[u] + 1;if(!st[y])q.push(y); }}}return d[t] != inf;
}
int bfs(int s,int mw,int t){if(s == t)return mw;for(int &i = cur[s];~i;i=ne[i]){int y = e[i];if(f[i] <= 0 || d[y] != d[s] + 1)continue;int cw = bfs(y,min(mw,f[i]),t);if(cw <= 0)continue;f[i] -= cw;f[i^1] += cw;return cw; }return 0;
}
int dinic(int s,int t){int flow = 0;while(dfs(s,t)){memcpy(cur,h,sizeof h);while(int d = bfs(s,inf,t)) flow += d;}return flow;
}
int main(){while(cin >> n >> n1 >> n2 >> m){idx = 0;memset(h,-1,sizeof h); int a,b,c;char t ; for(int i = 1;i<=m;i++){cin >> t >> a >> t >> b >> t >> c; add(a,b,c);}for(int i = 1;i<=n1;i++){cin >> t >> a >> t >> b;add(S,a,b);}for(int i = 1;i<=n2;i++){cin >> t >> a >> t >> b;add(a,T,b);}cout << dinic(S,T) << endl;    }return 0;
} 

.......................................................................................................................................................................................

[网络流24题]最小路径覆盖问题

题目链接

思路:

最小覆盖和二分图最大匹配等价的 问题转换为求二分图的最大匹配。
网络流求二分图的最大匹配

  • 将源点连上左边所有点,右边所有点连上汇点,容量皆为 1 。原来的每条边从左往右连边,容量也皆为1 ,最大流即最大匹配。

最小路径覆盖=总顶点数−最大匹配

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 350,M = 15100,inf = 0x3f;
int h[N],e[M],ne[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int pre[M],a[N];
bool st[M],vis[N];
int n,m;
int S = 0,T = N-10;
void EK(int s,int t){int flow = 0;while(1){memset(a,0,sizeof a);queue<int > q;q.push(s);a[s] = inf;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] > 0 && !a[y]){a[y] = min(f[i],a[u]);pre[y] = i;q.push(y);}}if(a[t])break;}if(!a[t])break;for(int i = t;i!=s;i=e[pre[i]^1]){f[pre[i]] -= a[t];f[pre[i]^1] += a[t];}}
}
void dfs(int u){vis[u] = true;cout << u << " ";for(int i = h[u];~i;i=ne[i]){if(st[i]){int y = e[i];dfs(y-n);// 1对1匹配不用找后面的了return ;}}
}
int main(){cin >> n >> m;int a,b;memset(h,-1,sizeof h);for(int i = 1;i<=m;i++){cin >> a >> b;add(a,b+n,1);}for(int i = 1;i<=n;i++){add(S,i,1);add(i+n,T,1);}EK(S,T);//标记匹配边 和 匹配数量int cnt = 0;for(int i = 0;i<idx;i+=2){if(e[i]==S || e[i^1] == S)continue;if(e[i]==T || e[i^1] == T)continue;if(f[i]==0){//流量被用 st[i] = true;cnt ++;}}for(int i = 1;i<=n;i++){if(!vis[i])dfs(i),cout << endl;}cout << n - cnt << endl;return 0;
}

P2765 魔术球问题

题目链接

思路:

把柱子上的数成点,每个柱子相当与一条路径,题目转换为: 让你求n条路径,使的路径上的总点数和最多。 这个和最小路径覆盖相似,给你n个点,求最小覆盖所有点的路径的个数。正好相反。
源点(S)(S)(S)相当于底,汇点(T)(T)(T)相当于顶部。对于一个数iii,加边S−>iS->iS−>i , i+delat−>Ti + delat - > Ti+delat−>T,当我们依此加入一个点(i)(i)(i)时,判断是不是和小于他的数(j)(j)(j)组成完全平方数(进行加边j−>i+delatj->i+delatj−>i+delat)。求一下当前完美匹配给个数,最小路径覆盖=总顶点数−最大匹配 判读是否继续加数。

代码代码代码

#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int N = 6000,M = 1e6+10,inf = 0x3ff;
int h[N],ne[M],e[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int pre[M],a[N];
bool vis[N];
bool st[M];
bool chick(int x){int k = sqrt(x);return k*k == x;
}
int n;
int S = 0,T = N - 1,delat = 3000;
int EK(int s,int t){int flow = 0;while(1){memset(a,0,sizeof a);a[s] = inf;queue<int> q;q.push(s);while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(!a[y] && f[i] > 0){a[y] = min(a[u],f[i]);q.push(y);pre[y] = i;}}if(a[t])break;}if(!a[t])break;for(int i = t;i!=s;i=e[pre[i]^1]){f[pre[i]] -= a[t];f[pre[i]^1] += a[t];}flow += a[t];}return flow;
}
void dfs(int u){vis[u] = true;cout << u << " ";for(int i = h[u];~i;i=ne[i]){if(st[i]){dfs(e[i] - delat);return ;}}
}
int main(){cin >> n; memset(h,-1,sizeof h);int cnt = 0;//匹配个数int p;for(p = 1;;p++){add(S,p,1);add(p+delat,T,1);for(int i = 1;i<p;i++){if(chick(i+p))add(i,p+delat,1);}cnt += EK(S,T);//最小路径覆盖if(p - cnt > n)break;memset(st,0,sizeof st);//记录匹配for(int i = 0;i<idx;i+=2){if(e[i]==S || e[i^1] == S)continue;if(e[i]==T || e[i^1] == T)continue;if(f[i]==0){//流量被用 st[i] = true;}}}cout << p-1 << endl;for(int j = 1;j<=p-1;j++){if(!vis[j])dfs(j),cout << endl;}return 0;
}

[网络流24题]最长递增子序列问题

题目链接
思路:

  1. 求最长上升子序列,le[i]le[i]le[i]表示iii位置序列最长长度。
  2. 建立边 : i−j(i>j)i - j (i>j)i−j(i>j)建立边条件 arr[i]>=arr[j]arr[i] >= arr[j]arr[i]>=arr[j] && le[i]=le[j]+1le[i] = le[j] + 1le[i]=le[j]+1
  3. 对与问题2 以 源点(S)(S)(S)向 长度为111的点设置流量111. 长度为最长子序列长度的点向汇点(T)(T)(T)流量111.
  4. 对于问题3 以 源点(S)(S)(S)向 长度为111的点设置流量infinfinf. 长度为最长子序列长度的点向汇点(T)(T)(T)流量infinfinf(注意最长子序列长度为111时流量为111.).

代码代码代码

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 1200,M = 1e6+10,inf = 0x3f3f;
int h[N],ne[M],e[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int pre[M],a[N];
int ans = 0;
int n,arr[N],le[N];
int S = 0 ,T = 501;
int EK(int s,int t){int flow = 0;while(1){memset(a,0,sizeof a);queue<int> q;q.push(s);a[s] = inf;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(!a[y] && f[i] > 0){a[y] = min(a[u],f[i]);pre[y] = i;q.push(y);}}if(a[t])break ;}if(!a[t])break;for(int i = t;i!=s;i=e[pre[i]^1]){f[pre[i]] -= a[t];f[pre[i]^1] += a[t];}flow += a[t];}return flow;
}
void slove(int val){memset(h,-1,sizeof h);idx = 0;for(int i = 1;i<=n;i++){if(!le[i])add(S,i,val);if(le[i] == ans)if(le[i] == 0)//最长为一个点,到汇点流量为1,表明只能用一下。add(i,T,1);else add(i,T,val);for(int j = i+1;j<=n;j++){if(arr[i] <= arr[j] && le[j] == le[i] + 1)add(i,j,1);}}cout << EK(S,T) << endl;
}
int main(){cin >> n ;for(int i = 1;i<=n;i++){cin >> arr[i];for(int j = 1;j<i;j++){if(arr[i] >= arr[j] && le[i] < le[j] + 1){le[i] = le[j] + 1;ans = max(le[i],ans);}}}cout << ans + 1 << endl;slove(1);slove(inf);return 0;
}

[CQOI2015]网络吞吐量

题目链接

思路:

由于数据从最最短上传输,先跑一边最短路,在最短路上建立边(这些边的流量为infinfinf),对于吞吐量(iii节点只身的限制内部建边) i−>i+ni - > i+ni−>i+n 加一条边流量为当前节点的吞吐量。
这里注意infinfinf 要大点

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
//526526
using namespace std;
const int N = 2020,M = 2e5+10;
const long long inf = 0x7f7f7f7f3f3f3f3f;
int h[N],ne[M],e[M],idx;
long long f[M];
void add(int a,int b,long long c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
long long dist[N];
bool st[N];
int n,m;
long long a[N],pre[N];
long long mp[N][N];
void spfa(){memset(dist,inf,sizeof dist);queue<int> q;q.push(1);st[1] = true;dist[1] = 0;while(!q.empty()){int u = q.front();q.pop();st[u] = false;for(int i = 1;i <=n ;i++){if(mp[u][i] && dist[i] > dist[u] + mp[u][i]){dist[i] = dist[u] + mp[u][i];if(!st[i]){q.push(i);st[i] = true;}}}}
}
long long EK(int s,int t){long long flow = 0;while(1){memset(a,0,sizeof a);queue<int> q;q.push(s);a[s] = inf;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(!a[y] && f[i] > 0){a[y] = min(f[i],a[u]);pre[y] = i;q.push(y);}  }if(a[t])break;}if(!a[t])break;for(int i = t;i!=s;i=e[pre[i]^1]){f[pre[i]] -= a[t];f[pre[i]^1] += a[t];}flow += a[t];}return flow;
}
int main(){cin >> n >> m;memset(h,-1,sizeof h);for(int i = 1;i<=m;i++){long long a,b,c;cin >> a >> b >> c;if(mp[a][b] == 0)mp[a][b] = mp[b][a] = c;elsemp[a][b] = mp[b][a] = min(mp[a][b],c);}spfa();long long tp;int s = 0, t = 1500;add(s,1,inf);add(n+n,t,inf);for(int i = 1;i<=n;i++){cin >> tp;if(i != 1 && i != n) add(i,i+n,tp);else add(i,i+n,inf);} for(int i = 1;i<=n;i++){for(int j = 1;j<=n;j++){if(mp[i][j] && dist[j] == dist[i] + mp[i][j])add(i+n,j,inf);}}cout << EK(s,t) << endl;return 0;
}

[SCOI2007]蜥蜴

题目链接

思路:

  • 对于每个点拆点(入点和出点),容量为高度。
  • 对于有高度的点向附近距离小于ddd的连边,出点连入店,容量为infinfinf。
  • 对于有壁虎的点,源点sss向该点入点连边,容量为infinfinf。
  • 对于能跑出棋盘的点(出点)向汇点连边,容量为infinfinf。

代码代码代码

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 1500,M = N*N,inf=0x3f3f3f;
int h[N],ne[M],e[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int pre[N],a[N];
int EK(int s,int t){int flow = 0;while(1){memset(a,0,sizeof a);queue<int> q;q.push(s);a[s] = inf;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] > 0 && !a[y]){a[y] = min(a[u],f[i]);pre[y] = i;q.push(y);}}if(a[t])break;}if(!a[t])break;for(int i = t;i!=s;i=e[pre[i]^1]){f[pre[i]] -= a[t];f[pre[i]^1] += a[t];}flow += a[t];}return flow;
}
int n,m,d,tp;
bool check(int x1,int y1,int x2,int y2){return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2) <= d*d;
}
int main(){cin >> n >> m >> d;int delat = (n+1)*m;int s = 0,t = 1;memset(h,-1,sizeof h); for(int i = 1;i<=n;i++){for(int j = 1;j<=m;j++){scanf("%1d",&tp);//只身拆点add(i*m+j,i*m+j+delat,tp);if(tp == 0)continue;for(int I=1;I<=n;I++)for(int J=1;J<=m;J++){if(check(i,j,I,J)){add(i*m+j+delat,I*m+J,inf);}}//init t;if(i <= d || n<d+i || j <= d || m<d+j)add(i*m+j+delat,t,inf);}}//init schar tp;int sum = 0;for(int i = 1;i<=n;i++){for(int j = 1;j<=m;j++){cin >> tp;if(tp == 'L')add(s,i*m+j,1),sum ++;}}cout << sum - EK(s,t) << endl;return 0;
}

PIGS

题目链接

建图:

对于第iii个人如果想买第jjj头猪圈的猪

  1. 如果jjj猪圈没被合并过,则从源点sss连向iii一条容量为猪圈大小的边。
  2. 如过jjj猪圈和并过,这从最后一次合并的那个人流向iii一条容量为infinfinf的边。

最后iii向汇点ttt连一个容量为购买猪数的边。

代码代码代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
//526526
using namespace std;
const int N = 2000,M =2e5+10,inf = 0x3f3f3f3f;
int h[N],ne[M],e[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int pre[N],a[N];
int EK(int s,int t){int flow = 0;while(1){memset(a,0,sizeof a);queue<int> q;q.push(s);a[s] = inf;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] > 0 && !a[y]){a[y] = min(a[u],f[i]);pre[y] = i;q.push(y);}}if(a[t])break;}if(!a[t])break;for(int i = t;i!=s;i=e[pre[i]^1]){f[pre[i]] -= a[t];f[pre[i]^1] += a[t];}flow += a[t];}return flow;
}
int n,m;
int id[N];
int d[N];
int main(){cin >> m >> n;int s = 0,t = N-1;memset(h,-1,sizeof h);for(int i = 1;i<=m;i++){cin >> d[i];} int k,tp; for(int i = 1;i<=n;i++){cin >> k;while(k-- ){cin >> tp;if(!id[tp])add(s,i,d[tp]);else add(id[tp],i,inf);id[tp] = i;}cin >> tp;add(i,t,tp);}cout << EK(s,t) << endl;return 0;
} 

[网络流24题]飞行员配对方案问题

题目链接
思路1:

二分图最大匹配。

代码代码代码

#include<iostream>
#include<cstring>
using namespace std;
const int N = 110,M = 2e5+10;
int h[N],ne[M],e[M],idx;
void add(int a,int b){ne[idx] = h[a],e[idx] = b,h[a] = idx++;
}
int match[N];
bool st[N];
int n,m;
bool find(int u){for(int i = h[u];~i;i=ne[i]){int y = e[i];if(st[y])continue;st[y] = true;if(!match[y] || find(match[y])){match[y] = u;return true;}}return false;
}
int main(){cin >> n >> m;int u,v;memset(h,-1,sizeof h);while(cin >> u >> v){if(u == -1)break;add(u,v);}int res = 0;for(int i = 1;i<=m;i++){memset(st,0,sizeof st);if(find(i))res++;}cout << res << endl;return 0;
}

网络流做法:

建图

  1. 源点sss向外籍飞行员建立一条容量为111的边。
  2. 英国飞行员向汇点ttt建立一条容量为111的边。
  3. 能匹配的飞行员之间建立一条容量为111的边。

代码代码代码

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 210,M = N*N,inf=0x3f3f3f;
int h[N],ne[M],e[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int pre[N],a[N];
int EK(int s,int t){int flow = 0;while(1){memset(a,0,sizeof a);queue<int> q;q.push(s);a[s] = inf;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] > 0 && !a[y]){a[y] = min(a[u],f[i]);pre[y] = i;q.push(y);}}if(a[t])break;}if(!a[t])break;for(int i = t;i!=s;i=e[pre[i]^1]){f[pre[i]] -= a[t];f[pre[i]^1] += a[t];}flow += a[t];}return flow;
}
int n,m;
int main(){cin >> n >> m;int u,v;int s = 0,t = 110;memset(h,-1,sizeof h);while(cin >> u >> v){if(u == -1)break;add(u,v,1);}for(int i = 1;i<=n;i++){if(i <= m)add(s,i,1);else add(i,t,1);}cout << EK(s,t) << endl;return 0;
}

[网络流24题]圆桌问题

题目链接

思路:

二分图的多重匹配转换为网络流。

  1. sss向代表连一条容量为代表人数的边。
  2. 餐桌向ttt连一条容量为餐桌大小的边。
  3. 代表向餐桌连一条容量为111的边。

代码代码代码

#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int N = 1000,M = N*N,inf=0x3f3f3f;
int h[N],ne[M],e[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int pre[N],a[N];
int EK(int s,int t){int flow = 0;while(1){memset(a,0,sizeof a);queue<int> q;q.push(s);a[s] = inf;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] > 0 && !a[y]){a[y] = min(a[u],f[i]);pre[y] = i;q.push(y);}}if(a[t])break;}if(!a[t])break;for(int i = t;i!=s;i=e[pre[i]^1]){f[pre[i]] -= a[t];f[pre[i]^1] += a[t];}flow += a[t];}return flow;
}
vector<int> ans[N];
int n,m;
int main(){cin >> n >> m;memset(h,-1,sizeof h);int tp,sum = 0;int s = 0,t = N - 1,delat = 200;for(int i = 1;i<=n;i++){cin >> tp;add(s,i,tp);sum += tp;}for(int i = 1;i<=m;i++){cin >> tp;add(i+delat,t,tp);for(int j = 1;j<=n;j++){add(j,i+delat,1);}}if(EK(s,t) == sum){cout << 1 << endl;for(int i = 0;i<idx;i+=2){if(e[i]==s || e[i^1] == s)continue;if(e[i]==t || e[i^1] == t)continue;if(f[i]==0){//流量被用 ans[e[i^1]].push_back(e[i]-delat);}}for(int i = 1;i<=m;i++){for(int j = 0;j<ans[i].size();j++)cout << ans[i][j] << " ";cout << endl;}}else{cout << 0 << endl;}return 0;
}

Fox And Dinner

题目链接

分析:

题目说一个环至少333个数,表明每个数都要有两两两个相邻的数。
和为质数,有a>2a>2a>2,所以任意两个数的和一定大于222,表明一定是一奇一偶的和。
建图

  1. sss向偶数建立一条容量为222的边,奇数向t建立一条容量为222的边。
  2. 和为质数的两个数连容量为111的一条边

代码代码代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
//526526
using namespace std;
const int N = 300,M = 4e5+10,inf = 0x3f3f3f3f;
int h[N],ne[M],e[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
vector<int> prime;
bool st[M],is_prime[M];
void getprime(int n = M){for(int i = 2;i<=n;i++){if(!st[i])prime.push_back(i),is_prime[i] = true;for(int j = 0;j<prime.size() && prime[j] * i< n;j++){st[i*prime[j]] = true;if(i % prime[j] == 0)break;}}
}
int dist[N],cur[N];
bool bfs(int s,int t){memset(dist,inf,sizeof dist);memset(st,0,sizeof st);queue<int> q;q.push(s);dist[s] = 0;st[s] = true;while(!q.empty()){int u = q.front();q.pop();st[u] = false;for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] > 0 && dist[y] > dist[u] + 1){dist[y] = dist[u] + 1;if(!st[y])q.push(y);} }}return dist[t] != inf;
}
int dfs(int u,int mv,int t){if(u == t)return mv;for(int &i = cur[u];~i;i=ne[i]){int y = e[i];if(f[i] <= 0 || dist[y] != dist[u] + 1)continue;int cw = dfs(y,min(mv,f[i]),t);if(cw <= 0)continue;f[i] -= cw;f[i^1] += cw;return cw;}return 0;
}
int dinic(int s,int t){int flow=0;while(bfs(s,t)){memcpy(cur,h,sizeof h);while(int d = dfs(s,inf,t))flow += d;}return flow;
}
int n;
int a[N];
int ans = 1;
vector<int> res[N];
int s,t;
void find(int u){res[ans].push_back(u);st[u] = true;for(int i = h[u];~i;i=ne[i]){if(e[i] == t || e[i^1] == t)continue;int j;if(a[u] & 1)j = i ^ 1;else j = i;  if(f[j] == 0 && !st[e[i]]){find(e[i]);} }
}
int main(){memset(h,-1,sizeof h);getprime();cin >> n;s=0,t=n+1;int even=0,odd=0;for(int i = 1;i<=n;i++){cin >> a[i];if(a[i]&1)add(i,t,2),odd++;else add(s,i,2),even++;for(int j = 1;j<i;j++){if(is_prime[a[i]+a[j]]){if(a[i]&1)add(j,i,1);else add(i,j,1);}}}if(even != odd || dinic(s,t) != n)printf("Impossible");else{    for(int i = 1;i<=n;i++){if(!st[i])find(i),ans++;}cout << ans-1 << endl;for(int i = 1;i<ans;i++){cout << res[i].size();for(int j = 0;j<res[i].size();j++)cout << " " << res[i][j] ;cout << endl;}}return 0;
}

Optimal Milking

题目链接
思路:

二分最大距离判断是否合适

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
//526526
using namespace std;
const int N = 300,M = 2*N*N,inf = 0x3f3f3f3f;
int h[N],ne[M],e[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int cur[N],dist[N];
bool st[N];
bool bfs(int s,int t){memset(dist,inf,sizeof dist);memset(st,0,sizeof st);queue<int> q;q.push(s);dist[s] = 0;st[s] = true;while(!q.empty()){int u = q.front();q.pop();st[u] = false;for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] > 0 && dist[y] > dist[u] + 1){dist[y] = dist[u] + 1;if(!st[y])q.push(y);}}}return dist[t] != inf;
}
int dfs(int u,int mv,int t){if(u == t)return mv;for(int &i = cur[u];~i;i=ne[i]){int y = e[i];if(f[i] <= 0 || dist[y] != dist[u] + 1)continue;int cw = dfs(y,min(f[i],mv),t);if(cw <= 0)continue;f[i] -= cw;f[i^1] += cw;return cw;}return 0;
}
int dinic(int s,int t){int flow=0;while(bfs(s,t)){memcpy(cur,h,sizeof h);while(int d = dfs(s,inf,t))flow += d;} return flow;
}
int n,m,d;
int mp[N][N];
bool check(int mid){memset(h,-1,sizeof h);idx = 0;int s = 0,t = n+m+1;for(int i = 1;i<=n;i++){add(i,t,d);}for(int i = n+1;i<=n+m;i++){add(s,i,1);}for(int i = 1;i<=n;i++){for(int j = n+1;j<=n+m;j++){if(mp[i][j] <= mid)add(j,i,1);}}return dinic(s,t) == m;
}
int main(){while(~scanf("%d%d%d",&n,&m,&d)){int t = n+m;for(int i = 1;i<=t;i++){for(int j = 1;j<=t;j++){cin >> mp[i][j];if(mp[i][j] == 0)mp[i][j] = inf;}}for(int k = 1;k<=t;k++)for(int i = 1;i<=t;i++)for(int j = 1;j<=t;j++)mp[i][j] = min(mp[i][j],mp[i][k] + mp[k][j]);int l = 0,r = inf;while(l < r){int  mid = l + r >> 1;if(check(mid))r = mid;else l = mid + 1;}cout << l << endl;}return 0;
}

Dining

题目链接

建图:

分为三部分:

  1. 左边为食物,s−>s - >s−> 食物容量为111.
  2. 右边为饮料,饮料−>t-> t−>t容量为111.
  3. 中间为牛,把牛拆点限制只能用一头,饮料−>->−>牛入点,牛入点−>->−>牛出点,牛出点−>->−>饮料.
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 500,M = 2*N*N,inf = 0x3f3f3f3f;
int h[N],ne[M],e[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int d[N],cur[N];
bool st[N];
bool bfs(int s,int t){memset(d,inf,sizeof d);memset(st,0,sizeof st);queue<int> q;q.push(s);st[s] = true;d[s] = 0;while(!q.empty()){int u = q.front();q.pop();st[u] = false;for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] > 0 && d[y] > d[u] + 1){d[y] = d[u] + 1;if(!st[y])q.push(y);}}}return d[t] != inf;
}
int dfs(int u,int mw,int t){if(u == t)return mw;for(int &i = cur[u];~i;i=ne[i]){int y = e[i];if(f[i] <= 0 || d[y] != d[u] + 1)continue;int cw = dfs(y,min(f[i],mw),t);if(cw == 0)continue;f[i] -= cw;f[i^1] += cw;return cw;}return 0;
}
int dinic(int s,int t){int flow = 0;while(bfs(s,t)){memcpy(cur,h,sizeof h);while(int d = dfs(s,inf,t))flow += d;}return flow;
}
int n,m,k;
int main(){cin >> n >> k >> m;int d1,f1,tp;memset(h,-1,sizeof h);for(int i = 1;i<=n;i++){cin >> d1 >> f1;for(int j = 1;j<=d1;j++){cin >> tp;add(tp,i+k+m,1);}for(int j = 1;j<=f1;j++){cin >> tp;add(i+k+m+n,tp+k,1);}add(i+k+m,i+k+m+n,1);}int s = 0,t = k+n+m+n+1;for(int i = 1;i<=k+m;i++){if(i <= k)add(s,i,1);else add(i,t,1);}cout << dinic(s,t) << endl;return 0;
}

[网络流24题]星际转移问题

题目链接

思路:

分层图

  1. 用(i,j)(i,j)(i,j),表示第iii天的jjj号太空站(包括地球和月球)
  2. 每天太空船经过的太空站(u−>v)(u->v)(u−>v) 连一条容量为太空船大小的边,(i−1,u)−>(i,v)(i-1,u) - > (i,v)(i−1,u)−>(i,v)。
  3. 每天s−>s->s−>地球(0)(0)(0)连一条容量为infinfinf的边,月球(n)(n)(n)向t连一条容量为infinfinf的边, (0,s)−>(i,0),(i,n)−>(0,t)(0,s) ->(i,0) , (i,n) - > (0,t)(0,s)−>(i,0),(i,n)−>(0,t)。
  4. 昨天的天空站向今天的天空站连一条容量infinfinf的边(代表太空站能容纳人), (i−1,j)−>(i,j)(i-1,j)->(i,j)(i−1,j)−>(i,j)。

代码代码代码

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N = 2000,M = N*N,inf  = 0x3f3f3f3f;
int h[N],ne[M],e[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int d[N],cur[N];
bool st[N];
int flow = 0;
bool bfs(int s,int t){memset(d,inf,sizeof d);memset(st,0,sizeof st);queue<int> q;q.push(s);st[s] = true;d[s] = 0;while(!q.empty()){int u = q.front();q.pop();st[u] = false;for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] > 0 && d[y] > d[u] + 1){d[y] = d[u] + 1;if(!st[y])q.push(y);}}}return d[t] != inf;
}
int dfs(int u,int mw,int t){if(u == t)return mw;for(int &i = cur[u];~i;i=ne[i]){int y = e[i];if(f[i] <= 0 || d[y] != d[u] + 1)continue;int cw = dfs(y,min(f[i],mw),t);if(cw == 0)continue;f[i] -= cw;f[i^1] += cw;return cw;}return 0;
}
int dinic(int s,int t){while(bfs(s,t)){memcpy(cur,h,sizeof h);while(int d = dfs(s,inf,t))flow += d;}return flow;
}
int n,m,k;
vector<int> a[30];
int point[30],w[30];
int ans = 0;
int s = N - 2,t = N - 1,tp;
int main(){cin >> n >> m >> k;n+=2;memset(h,-1,sizeof h);for(int i = 1;i<=m;i++){cin >> w[i] >> tp;for(int j = 1;j<=tp;j++){int x;cin >> x;x ++;if(x == 0)x = n;a[i].push_back(x);}}add(s,1,inf);add(n,t,inf);while(++ans < 50){add(s,1 + n*ans,inf);add(n*ans+n,t,inf);for(int i = 1;i<=m;i++){int x = a[i][point[i]],y;if(point[i] + 1 >= a[i].size())point[i] = 0;else point[i] ++;y = a[i][point[i]];add(n*ans+x-n,y+n*ans,w[i]);}for(int i = 1;i<n;i++){add(n*ans+i-n,ans*n+i,inf);}dinic(s,t);if(flow >= k)break;}if(ans == 50)cout << 0 << endl;else cout << ans << endl;return 0;
}

[网络流24题]方格取数问题

题目链接

最小割

oi wiki
割¶

对于一个网络流图 GGG,其割的定义为一种 点的划分方式:将所有的点划分为 SSS 和 T=G−ST=G-ST=G−S 两个集合,其中源sss属于SSS点 ,汇点ttt属于TTT 。

容量¶

割 (S,T)(S,T)(S,T)的容量 表示所有从 SSS 到 TTT 的边的容量之和c(S,T)c(S,T)c(S,T).

最小割¶

最小割就是求得一个割(S,T)(S,T)(S,T)使得割的容量c(S,T)c(S,T)c(S,T)最小. (就是一个使得断掉的边权值和最小的划分方案)。

最大流最小割定理

  • f(s,t)max=c(s,t)minf(s,t)max = c(s,t)minf(s,t)max=c(s,t)min;

问题模型¶

有 nnn 个物品和两个集合 A,BA,BA,B ,如果将一个物品放入 AAA 集合会花费 aiaiai,放入 BBB 集合会花费 bibibi;还有若干个形如 uuu 和 vvv 同时不在一个集合会花费 wiwiwi 。每个物品 必须且只能属于一个集合,求最小的代价。

建图¶

每个集合设置源点 sss 和汇点 t,第 iii 个点由 sss 连一条容量为 aiaiai 的边、向 ttt 连一条容量为 bibibi 的边。对于限制条件 ,在 u,vu,vu,v 之间连容量为 wiwiwi 的双向边。

...................................分割线................................................................................................................

本题思路:

二分图点权最大独立集

带点权二分图GGG中的一个子集VVV,其中一条边的两个端点不能同时属于VVV,且VVV中点权和最大。
点覆盖集的补集就是独立集,因为点覆盖集中每个边都至少被一个点覆盖,补集就不可能存在一条边的两个端点。

那么显然:二分图点权最大独立集=二分图点权和-二分图最小点权覆盖集(最小的点覆盖最多的边)

在二分图中,有一个特殊的性质:当所有点权都为1时,最大匹配数 = 最小权点覆盖集;最大权独立集 = n - 最小权点覆盖集。

上面的模型是说都要选,该题是不能选相邻的,可以将相邻的(黑白染色)建立一条容量为infinfinf的边,sss向白点连一条容量为ai,jai,jai,j的边,黑点向ttt连一条容量为ai,jai,jai,j的边。

代码代码代码

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 10100,M = 1e6+10,inf=0x3f3f3f;
int h[N],ne[M],e[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int pre[N],a[N];
int EK(int s,int t){int flow = 0;while(1){memset(a,0,sizeof a);queue<int> q;q.push(s);a[s] = inf;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] > 0 && !a[y]){a[y] = min(a[u],f[i]);pre[y] = i;q.push(y);}}if(a[t])break;}if(!a[t])break;for(int i = t;i!=s;i=e[pre[i]^1]){f[pre[i]] -= a[t];f[pre[i]^1] += a[t];}flow += a[t];}return flow;
}
int n,m;
int main(){int tp,s = 0,t=1,sum=0;cin >> n >> m;memset(h,-1,sizeof h);for(int i = 1;i<=n;i++){for(int j = 1;j<=m;j++){cin >> tp;sum+=tp;if((i+j)&1){add(s,i*m+j,tp);if(i - 1 >= 1)add(i*m+j,(i-1)*m+j,inf);if(i + 1 <= n)add(i*m+j,(i+1)*m+j,inf);if(j - 1 >= 1)add(i*m+j,i*m+j-1,inf);if(j + 1 <= m)add(i*m+j,i*m+j+1,inf);}else{add(i*m+j,t,tp);}}}cout << sum - EK(s,t) << endl;return 0;
}

最大获利

题目链接

最大权图

概念¶

有一个有向图,每一个点都有一个权值(可以正负),选择一个权值和最大的子图,使得每一个点的后继都在子图里面,这个子图就叫最大权闭合子图。

解决方法¶

从源点s向每个正权点连一条容量为权值的边,每个负权点向汇点t连一条容量为权值的绝对值的边,有向图原来的边容量为无穷大。>求他的最小割,割掉后,于源点s联通的点构成最大权闭合子图,权值为(正权值之和-最小割).

题目代码。

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 60000,M = 2e6+10,inf = 0x3f3f3f3f;
int h[N],e[M],ne[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int cur[N],d[N];
bool bfs(int s,int t){memset(d,0,sizeof d);queue<int> q;q.push(s);d[s] = 1;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] && !d[y])d[y] = d[u] + 1,q.push(y);}}return d[t];
}
int dfs(int u,int t,int mw){if(u == t)return mw;for(int &i=cur[u];~i;i=ne[i]){int y = e[i];if(f[i] <= 0 || d[y] != d[u] + 1)continue;int cw=dfs(y,t,min(f[i],mw));if(cw == 0)continue;f[i] -= cw;f[i^1] += cw;return cw;}return 0;
}
int dinic(int s,int t){int flow = 0;while(bfs(s,t)){memcpy(cur,h,sizeof cur);while(int d = dfs(s,t,inf))flow += d;}return flow;
}
int n,m;
int main(){cin >> n >> m;int s = 0 , t=n+m+1,tp;memset(h,-1,sizeof h);for(int i = 1;i<=n;i++)cin >> tp,add(s,i,tp);int sum = 0;for(int i = 1;i<=m;i++){int a,b,c;cin >> a >> b >> c;add(a,++n,inf);add(b,n,inf);add(n,t,c);sum += c;}cout << sum - dinic(s,t) << endl;return 0;
}

Magic Slab

题目链接

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int N = 4e4+10,M = 2e6+10,inf = 0x3f3f3f3f;
int h[N],e[M],ne[M],f[M],idx;
void add(int a,int b,int c){ne[idx] = h[a],e[idx] = b,f[idx] = c,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,h[b] = idx++;
}
int cur[N],d[N];
bool bfs(int s,int t){memset(d,0,sizeof d);queue<int> q;q.push(s);d[s] = 1;while(!q.empty()){int u = q.front();q.pop();for(int i = h[u];~i;i=ne[i]){int y = e[i];if(f[i] && !d[y])d[y] = d[u] + 1,q.push(y);}}return d[t];
}
int dfs(int u,int t,int mw){if(u == t)return mw;for(int &i=cur[u];~i;i=ne[i]){int y = e[i];if(f[i] <= 0 || d[y] != d[u] + 1)continue;int cw=dfs(y,t,min(f[i],mw));if(cw == 0)continue;f[i] -= cw;f[i^1] += cw;return cw;}return 0;
}
int dinic(int s,int t){int flow = 0;while(bfs(s,t)){memcpy(cur,h,sizeof cur);while(int d = dfs(s,t,inf))flow += d;}return flow;
}
int n,m;
int main(){cin >> n >> m;int tp;int s = 0 ,t = 1;long long sum=0;memset(h,-1,sizeof h);for(int i = 1;i<=n;i++)for(int j = 1;j<=n;j++)cin >> tp,add(i*n+j,t,tp),add(n*(n+1)+i,i*n+j,inf),add(n*(n+2)+j,i*n+j,inf),sum += tp;for(int i = 1;i<=n;i++)cin >> tp,add(s,n*(n+1) + i,tp);for(int i = 1;i<=n;i++)cin >> tp,add(s,n*(n+2) + i,tp);for(int i = 1;i<=m;i++){int a,b,c,d,w;cin >> a >> b >> c >> d >> w;sum += w;add(n*(n+1) + a,n*(n+3)+i , inf);add(n*(n+1) + c,n*(n+3)+i , inf);add(n*(n+2) + b,n*(n+3)+i , inf);add(n*(n+2) + d,n*(n+3)+i , inf);add(n*(n+3) + i , t , w);}cout << sum - dinic(s,t) << endl;return 0;
}

网络流24题]餐巾计划问题

题目链接
参考链接

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 4100,M = 4e6+10,inf = 0x3f3f3f3f;
const long long inf64 = 23333333333333;
int h[M],e[M],ne[M],idx;
long long f[M],w[M];
void add(int a,int b,long long c,long long d){ne[idx] = h[a],e[idx] = b,f[idx] = c,w[idx] = d,h[a] = idx++;ne[idx] = h[b],e[idx] = a,f[idx] = 0,w[idx] = -d;h[b] = idx++;
}
int cur[M];
long long dist[M];
bool st[M];
long long cost;
bool spfa(int s,int t){for(int i = 0;i<N;i++)dist[i] = inf64,st[i] = 0;dist[s] = 0;st[s] = true;queue<int> q;q.push(s);while(!q.empty()){int u = q.front();q.pop();st[u] = false;for(int i = h[u];~i;i=ne[i]){int y = e[i];if(dist[y] > dist[u] + w[i] && f[i]){dist[y] = dist[u] + w[i];if(!st[y])st[y] = true,q.push(y);}}//cout <<u << endl;}return dist[t] != inf64;
}
long long dfs(int u,int t,long long mw){if(u == t)return mw;st[u] = true;for(int &i = cur[u];~i;i=ne[i]){int y = e[i];if(f[i] <= 0 || dist[y] != dist[u] + w[i] || st[y])continue;long long cw = dfs(y,t,min(f[i],mw));if(cw <= 0)continue;f[i] -= cw;f[i^1] += cw;cost += (long long)w[i] * cw;return cw;}st[u] = false;return 0;
}
long long dinic(int s,int t){while(spfa(s,t)){memcpy(cur,h,sizeof cur);while(dfs(s,t,inf64));}return cost;
}
long long N_N,p,m,f_f,n,s_s;
int main(){cin >> N_N >> p >> m >> f_f >> n >> s_s;int s = 0,t = N_N * 2 + 1,tp;memset(h,-1,sizeof h);for(int i = 1;i<=N_N;i++){cin >> tp;add(s,i,inf,p);    // sadd(i,t,tp,0); // t;add(s,i+N_N,tp,0);//慢洗;if(N_N >= i + n)add(i+N_N,i+n,inf64,s_s);//快洗if(N_N >= i + m)add(i+N_N,i+m,inf64,f_f);//不洗if(N_N >= i + 1)add(i+N_N,i+N_N+1,inf64,0);}cout << dinic(s,t) << endl;return 0;
}

E. Goods transportation

题目链接

思路:

  • 考虑网络流建图:对于每个点iii,从SSS到连一条容量为pipipi的路径,从iii到TTT连一条容量为sisisi的路径。然后每个点又向比自己大的点连一条容量为ccc的路径。
    由于边的数量过多,需要的时间太多。
  • 考虑到最大流等于最小割。
  • 用 dp[i][j]dp[i][j]dp[i][j] 表示遍历到第iii个点,jjj个点属于SSS的最小割。
  • 状态转移 : 对于第iii个点属于SSS, dp[i][j]=dp[i−1][j−1]+s[i];dp[i][j] = dp[i-1][j-1] + s[i] ;dp[i][j]=dp[i−1][j−1]+s[i]; // 断掉到TTT的边
    对于第iii个点不属于SSS,dp[i]j]=dp[i−1][j]+p[i]+c∗j;dp[i]j] = dp[i-1][j] + p[i] + c*j;dp[i]j]=dp[i−1][j]+p[i]+c∗j; //断掉SSS和属于SSS点到iii的边。
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e4+10;
long long f[2][N];int n;
long long c;
long long p[N],s[N];
int main(){cin >> n >> c;for(int i = 1;i<=n;i++)cin >> p[i];for(int i = 1;i<=n;i++)cin >> s[i];for(int i = 0;i<=n;i++)f[0][i] = f[1][i] = 9e18;int pre = 0;f[1][0] = 0;for(int i = 1;i<=n;i++){f[pre][0] = f[pre^1][0] + p[i];for(int j = 1;j<=i;j++)f[pre][j] = min(f[pre^1][j-1] + s[i] , f[pre^1][j] + p[i] + c*j);pre ^= 1;}long long ans = 9e18;//for(int i = 1;i<=n;i++)cout << f[pre^1][i] << endl;for(int i = 0;i<=n;i++)ans = min(ans,f[pre^1][i]);cout << ans << endl;return 0;
}

网络流专题班例题和练习相关推荐

  1. 牛客竞赛数学专题班生成函数I 题解

    牛客竞赛数学专题班生成函数I 题解 题单链接 背包 题目链接 题意 总共有888个物品,对于每个物品的选法都有要求,问带nnn个物品的方案数. 思路 构造生成函数,并将等比级数转为合式(∏i=0xi= ...

  2. 数据结构视频教程 -《数据结构深度实战专题班 C语言版(国嵌 唐老师主讲)(非常犀利)》

    整个视频打包下载地址:史上最全的数据结构视频教程系列分享之<数据结构深度实战专题班 C语言版(国嵌 唐老师主讲)(非常犀利)>,转载请保留出处和链接! 更多优秀资源请访问:我是码农 在计算 ...

  3. 图论专题班连通性例题和练习

    2022牛客 图论 连通性(强连通.割点和桥) 文章目录 2022牛客 图论 连通性(强连通.割点和桥) 概念 双连通分量 强连通分量 tarjan 割点 割边(桥) tarjan求割点,桥 点双联通 ...

  4. 网络流专题(最大流与费用流)(一)

    流量网络 • 想要将一些水从S运到T,必须经过一些水站,链接水站的是管道,每条管道都有它的最大能容纳的水量,求最多S到T能流多少流量. 基本概念 • 这是一个典型的网络流模型.我们先了解网络流的有关定 ...

  5. 网络流专题(完结撒花)

    题目及链接地址 TOJ4085: Drainage Ditcheshttp://210.32.82.1/acmhome/showstatus.do?&page=1&problemId= ...

  6. 学大伟业(杭州分校)数学联赛 GA3-1 国奥专题班

    北京学大伟业(bjxdwy)杭州分校2019课程于4月4日至4月7日开课,名师生齐聚课堂,助力2019五项学科竞赛!冲刺c9名校!

  7. 数据结构深度实战专题班 C语言版(国嵌 唐老师主讲)(非常犀利)

    在计算机科学中,数据结构是一门研究非数值计算的程序设计问题中计算机的操作对象(数据元素)以及它们之间的关系和运算等的学科,数据结构是计算机专业的必修课程之一,也是编写高性能算法的必要前提.本视频教程的 ...

  8. 牛客竞赛数据结构专题班树状数组、线段树练习题

    F.little w and Discretization 题意:找区间[l,r]内离散化后和原来的值不同大小的数的个数 思路:先求区间mex,同时记录区间有多少个数,再 用区间长度减去(区间内小于m ...

  9. 网络流及建模专题(上)

    前言 不断更新中-- 这几天新坑填不下去了,回来回顾一些经典的模型套路,先拿网络流开刀,窃以为洛谷这几道网络流的题目还是非常具有代表性的,涵盖了网络流调整.多解计数.最小割.最大权闭合子图问题. 还涵 ...

最新文章

  1. Using Apache2 with JBoss AS7 on Ubuntu
  2. linux 在硬盘中创建文件系统,linux mkfs命令创建Linux文件系统
  3. spacy如何安装最匹配的版本正规文档en_core_web
  4. 关于TensorFlow,你应该了解这9件事(附代码链接)
  5. npm 更改默认全局路径以及国内镜像
  6. 最详细的U-BOOT源码分析及移植
  7. js打印(控件)及多种方式
  8. 基于ssm柴犬主题咖啡厅系统
  9. ADS仿真功率放大器模型导入报错问题解决
  10. Word文档的使用技巧
  11. 小米平板2Android分区表,DIY:8G+128G双系统小米平板2
  12. 关于某normal大学数据库登录的一个尝试
  13. Django入门超easy系列(一)——— 从一个简单的例子入门
  14. mysql 嵌套查询性能_MySQL数据库之嵌套查询与连接查询的性能详解
  15. 软件工程小组项目——单词计数
  16. 强生成首家上海市政府认定外资开放式创新平台;因美纳与红杉中国在华共推基因组学孵化器 | 美通企业日报...
  17. 微信开发者工具 设置一个tab为4个空格
  18. 【IOS】最简单方式实现跑马灯文字效果
  19. 个人喜欢的学习SAP的书
  20. 1146 -table 'performance_schema.session_variables' donesn't exist解决方案

热门文章

  1. JS中函数的返回值介绍
  2. 小孩学计算机技术有什么好处,儿童学习电脑的好处
  3. 电脑一启动吃鸡就重启计算机,租号器登录电脑重启-租号玩绝地求生提示登录出现异请重启客户端...
  4. 计算机电源怎么设置玩游戏不卡,端游绝地求生怎么设置不卡
  5. Github 上的个人项目开源心得
  6. Android 天气APP(二十七)增加地图天气的逐小时天气、太阳和月亮数据
  7. mysql优化总结(四)
  8. 南京邮电大学离散数学实验三(传递性,自反性,对称性)
  9. Linux系统调用列表
  10. 360安全卫士弹窗广告怎么彻底关闭