网络流入门专题(洛谷题单)
网络流入门到入土(#1)
最大流:
裸最大流:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; //const int maxm = 1e3 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; int m,n,s,t, x, head[50000], num_edge; int cur[50000],deep[50000],last[50000],num[50000]; //cur当前弧优化; last该点的上一条边; num桶 用来GAP优化 struct Edge{ int next,to,dis; }edge[400000]; void add_edge(int from,int to,int dis) { edge[num_edge].to=to; edge[num_edge].dis=dis; edge[num_edge].next=head[from]; head[from]=num_edge++; } //bfs仅用于更新deep void bfs(int t) { queue<int>q; for (int i=0; i<=n; i++) cur[i]=head[i]; for (int i=1; i<=n; i++) deep[i]=n; deep[t]=0; q.push(t); while (!q.empty()) { int now=q.front(); q.pop(); for (int i=head[now]; i != -1; i=edge[i].next) { if (deep[edge[i].to]==n && edge[i^1].dis)//i^1是为了找反边 { deep[edge[i].to] = deep[now]+1; q.push(edge[i].to); } } } } int add_flow(int s,int t) { int ans=inf,now=t; while (now!=s) { ans=min(ans,edge[last[now]].dis); now=edge[last[now]^1].to; } now=t; while (now!=s) { edge[last[now]].dis-=ans; edge[last[now]^1].dis+=ans; now=edge[last[now]^1].to; } return ans; } int isap(int s,int t) { int now=s; int maxflow = 0; bfs(t);//搜出一条增广路 for (int i=1; i<=n; i++) num[deep[i]]++; while (deep[s]<n) { if (now==t) {//如果到达汇点就直接增广,重新回到源点进行下一轮增广 maxflow+=add_flow(s,t); now=s;//回到源点 } bool has_find=0; for (int i=cur[now]; i!=-1; i=edge[i].next) { if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路 { has_find=true; cur[now]=i;//当前弧优化 now=edge[i].to; last[edge[i].to]=i; break; } } if (!has_find)//没有找到出边,重新编号 { int minn=n-1; for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径 if (edge[i].dis) minn=min(minn,deep[edge[i].to]); if ((--num[deep[now]])==0) break;//GAP优化 出现了断层 num[deep[now]=minn+1]++; cur[now]=head[now]; if (now!=s) now=edge[last[now]^1].to; } } return maxflow; } int main(){ num_edge = 0; memset(head,-1,sizeof(head)); scanf("%d%d%d",&n,&m,&x); for(int i=0;i<m;i++){ int u,v,cap; scanf("%d%d%d",&u,&v,&cap); add_edge(u,v,cap); add_edge(v,u,0); } s = 1, t = n; int ans = isap(s,t); if (!ans) cout << "Orz Ni Jinan Saint Cow!" << endl; else { int ans2 = ceil(x*1./ans); printf("%d %d\n", ans, ans2); } return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; //const int maxm = 1e3 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; int m,n,s,t, x, head[maxn], num_edge; int cur[maxn],deep[maxn],last[maxn],num[maxn]; int node_num; //cur当前弧优化; last该点的上一条边; num桶 用来GAP优化 struct Edge{ int next,to,dis; }edge[maxn*2]; void add_edge(int from,int to,int dis) { edge[num_edge].to=to; edge[num_edge].dis=dis; edge[num_edge].next=head[from]; head[from]=num_edge++; } //bfs仅用于更新deep void bfs(int t) { queue<int>q; for (int i=0; i<=node_num; i++) cur[i]=head[i]; for (int i=1; i<=node_num; i++) deep[i]=node_num; deep[t]=0; q.push(t); while (!q.empty()) { int now=q.front(); q.pop(); for (int i=head[now]; i != -1; i=edge[i].next) { if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边 { deep[edge[i].to] = deep[now]+1; q.push(edge[i].to); } } } } int add_flow(int s,int t) { int ans=inf,now=t; while (now!=s) { ans=min(ans,edge[last[now]].dis); now=edge[last[now]^1].to; } now=t; while (now!=s) { edge[last[now]].dis-=ans; edge[last[now]^1].dis+=ans; now=edge[last[now]^1].to; } return ans; } int isap(int s,int t) { int now=s; int maxflow = 0; bfs(t);//搜出一条增广路 for (int i=1; i<=node_num; i++) num[deep[i]]++; while (deep[s]<node_num) { if (now==t) {//如果到达汇点就直接增广,重新回到源点进行下一轮增广 maxflow+=add_flow(s,t); now=s;//回到源点 } bool has_find=0; for (int i=cur[now]; i!=-1; i=edge[i].next) { if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路 { has_find=true; cur[now]=i;//当前弧优化 now=edge[i].to; last[edge[i].to]=i; break; } } if (!has_find)//没有找到出边,重新编号 { int minn=node_num-1; for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径 if (edge[i].dis) minn=min(minn,deep[edge[i].to]); if ((--num[deep[now]])==0) break;//GAP优化 出现了断层 num[deep[now]=minn+1]++; cur[now]=head[now]; if (now!=s) now=edge[last[now]^1].to; } } return maxflow; } int main(){ num_edge = 0; memset(head,-1,sizeof(head)); scanf("%d %d", &n, &m); node_num = m; s = 1, t = m; for (int i = 1, u, v, w; i <= n; ++ i) { scanf("%d %d %d", &u, &v, &w); add_edge(u, v, w), add_edge(v, u, 0); } int ans = isap(s,t); printf("%d\n", ans); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, S, T;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline int dfs(int u, int flow){ if(u == T) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } int cap[maxn]; int vis[maxn]; int main() { init(); scanf("%d %d", &m, &n); S = 0, T = n+1; node_num = T; for (int i = 1; i <= m; ++ i) scanf("%d", &cap[i]); for (int i = 1; i <= n; ++ i){ int num; scanf("%d", &num); for (int j = 1; j <= num; ++j) { int temp; scanf("%d", &temp); if (!vis[temp]) vis[temp] = i, Add_Edge(S, i, cap[temp]); else Add_Edge(vis[temp], i, inf); vis[temp] = i; } int val; scanf("%d", &val); Add_Edge(i, T, val); } printf("%d\n", Dinic()); return 0; }
点限制最大流(拆点最大流
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, s, t, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; } inline bool Bfs(){ for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[t] != -1; } inline int dfs(int u, int flow){ if(u == t) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 1; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(s, inf); } return ans; } int main(){ int cnt = 0; memset(head, -1, sizeof(head)); int n1, n2, n3; scanf("%d %d %d", &n1, &n2, &n3); //书,练习册,答案 s = 1, t = n1*2+n2+n3+2; node_num = t; for (int i = 1; i <= n1; ++ i) { int book1 = (i-1)*2+1+1, book2 = book1 + 1; Add_Edge(book1, book2, 1), Add_Edge(book2, book1, 0); //cout << book1 << "---" << book2 << endl; } for (int i = 1; i <= n2; ++ i) { int v = i + 2*n1+1; Add_Edge(s, v, 1), Add_Edge(v, s, 0); //cout << s << "---" << v << endl; } for (int i = 1; i <= n3; ++ i) { int v = n1*2+n2+i+1; Add_Edge(v, t, 1), Add_Edge(t, v, 0); //cout << v << "---" << t << endl; } int m1, m2; scanf("%d", &m1); for (int i = 1, u, v; i <= m1; i++) { scanf("%d %d", &u, &v); int book1 = (u-1)*2+1+1; v = n1*2+1+v; Add_Edge(v, book1, 1), Add_Edge(book1, v, 0); //cout << v << "---" << book1 << endl; } scanf("%d", &m2); for (int i = 1, u, v; i <= m2; i++) { scanf("%d %d", &u, &v); int book2 = (u-1)*2+1+1+1, an = v+n1*2+n2+1; Add_Edge(book2, an, 1), Add_Edge(an, book2, 0); //cout << book2 << "---" << an << endl; } printf("%d\n", Dinic()); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, s, t, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; } inline bool Bfs(){ for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[t] != -1; } inline int dfs(int u, int flow){ if(u == t) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 1; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(s, inf); } return ans; } int main(){ cnt = 0; memset(head, -1, sizeof(head)); int n1, n2, n3; scanf("%d %d %d", &n1, &n2, &n3); //书,练习册,答案 s = 1, t = n1*2+n2+n3+2; node_num = t; for (int i = 1; i <= n1; ++ i) { int people1 = (i-1)*2+1+1, people2 = people1 + 1; Add_Edge(people1, people2, 1), Add_Edge(people2, people1, 0); } for (int i = 1; i <= n2; ++ i) { int v = i + 2*n1+1; Add_Edge(s, v, 1), Add_Edge(v, s, 0); } for (int i = 1; i <= n3; ++ i) { int v = n1*2+n2+i+1; Add_Edge(v, t, 1), Add_Edge(t, v, 0); } for (int i = 1; i <= n1; i++) { for (int j = 1, temp; j <= n2; ++ j) { scanf("%d", &temp); if (!temp) continue; int people1 = (i-1)*2+1+1, v = n1*2+1+j; Add_Edge(v, people1, 1), Add_Edge(people1, v, 0); } } for (int i = 1; i <= n1; i++) { for (int j = 1, temp; j <= n3; ++ j) { scanf("%d", &temp); if (!temp) continue; int people2 = (i-1)*2+1+1+1, an = j+n1*2+n2+1; Add_Edge(people2, an, 1), Add_Edge(an, people2, 0); } } printf("%d\n", Dinic()); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e7 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, s, t, k, r, d;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[t] != -1; } inline int dfs(int u, int flow){ if(u == t) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 1; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(s, inf); } return ans; } inline int id1(int x, int y) { return (x-1)*m+y; } inline int id2(int x, int y) { return (x-1)*m+y+n*m; } inline int cal(int x1, int y1, int x2, int y2) { return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2); } char g[205][205]; char gg[205][205]; int id[205][205]; char str[30]; int main() { cnt = 0; memset(head, -1, sizeof(head)); scanf("%d%d%d", &n, &m, &d); s = 1, t = n*m*2+2;//每个点拆成两个点,表示从石柱顶连向石柱下端 node_num = t; for(int i = 1; i <= n; i ++) { scanf("%s", str+1); for(int j = 1; j <= m; j ++) if(str[j]-'0' > 0) Add_Edge(id1(i, j)+1, id2(i, j)+1, str[j]-'0'); }//这个点有石柱,石柱顶连向石柱下端,容量为石柱高 int res = 0; for(int i = 1; i <= n; i ++) { scanf("%s", str+1); for(int j = 1; j <= m; j ++) if(str[j] == 'L') Add_Edge(s, id1(i, j)+1, 1), res ++; }//这个点有蜥蜴,源点连向石柱的上端 for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) if(i-d < 1 || i+d > n || j-d < 1 || j+d > m) Add_Edge(id2(i, j)+1, t, inf);//可以跳出去,石柱的底连向汇点 for(int x1 = 1; x1 <= n; x1 ++)//距离小于等于d,可以跳到的两个点连边 for(int y1 = 1; y1 <= m; y1 ++) for(int x2 = 1; x2 <= n; x2 ++) for(int y2 = 1; y2 <= m; y2 ++) if(cal(x1, y1, x2, y2) <= d*d) Add_Edge(id2(x1, y1)+1, id1(x2, y2)+1, inf); printf("%d\n", res-Dinic()); return 0; }
这一题的输出方式值得记住;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, S, T, k, d;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline int dfs(int u, int flow){ if(u == T) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } int main() { init(); n = read(), m = read(); S = 0, T = n+m+1; int sum = 0; node_num = T; for (int i = 1, w; i <= n; ++ i) { scanf("%d", &w); Add_Edge(S, i, w); sum += w; } for (int i = 1; i <= m; ++ i) { int num; scanf("%d", &num); Add_Edge(i+n, T, 1); for (int j = 1; j <= num; ++ j) { int temp; scanf("%d", &temp); Add_Edge(temp, i+n, 1); } } if (Dinic() != sum) cout << "No Solution!" << endl; else { for (int i = 1; i <= n; ++ i){ cout << i << ":"; for (int j = head[i]; ~j; j = edge[j].next) if (edge[j].to > 0 && !edge[j].w) printf(" %d", edge[j].to-n); cout << endl; } } return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, S, T, f, d;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline int dfs(int u, int flow){ if(u == T) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } int main() { init(); scanf("%d %d %d", &n, &f, &d); S = 0, T = f+d+2*n+1; node_num = T; int sum = 0; for (int i = 1; i <= f; ++i) Add_Edge(S, i+n*2, 1); for (int i = 1; i <= d; ++i) Add_Edge(i+f+n*2, T, 1); for (int i = 1; i <= n; ++ i) { int fi, di; Add_Edge((i-1)*2+1, (i-1)*2+2, 1); scanf("%d %d", &fi, &di); for (int j = 1, tf; j <= fi; ++ j) { scanf("%d", &tf); Add_Edge(tf+n*2, (i-1)*2+1, 1); } for (int j = 1, td; j <= di; ++ j) { scanf("%d", &td); Add_Edge((i-1)*2+2, td+f+n*2, 1); } } printf("%d\n", Dinic()); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; #define pii pair<long long, int> typedef long long ll; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const ll inf = 1e18; const int mod = 1e9 + 7; struct Edge{ int to; int next; ll w; } edge[maxn*2]; int head[maxn], node_num; int cur[maxn];//当前弧优化数组 int n, m, S, T, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 bool vis[maxn]; inline void init(){ cnt = 0; for (int i = 0; i <= node_num; ++ i) head[i] = -1; } inline void Add_Edge(int u, int v, ll w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline ll dfs(int u, ll flow){ if(u == T) return flow; ll del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 ll ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline ll Dinic() { ll ans = 0; while(Bfs()) { for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } int main() { scanf("%d %d", &m, &n); S = 0, T = n+m+1; node_num = T; init(); int rec_n[n+1], rec_m[m+1]; int sum = 0; for (int i = 1; i <= m; ++ i) { scanf("%d", &rec_m[i]); sum += rec_m[i]; Add_Edge(S, i, rec_m[i]); for (int j = 1; j <= n; ++ j) Add_Edge(i, j+m, 1); } for (int i = 1; i <= n; ++ i) { scanf("%d", &rec_n[i]); Add_Edge(i+m, T, rec_n[i]); } int flag = 0; if (Dinic() == sum) flag = 1; cout << flag << endl; if (flag) { vector<int> rec[m+1]; for (int u = 1; u <= m; ++ u) { for (int j = head[u]; ~j; j = edge[j].next) { if (edge[j].w == 0) { rec[u].push_back(edge[j].to-m); } } } for (int i = 1; i <= m; ++ i) { cout << rec[i][0]; for (int j = 1; j < rec[i].size(); ++ j) cout << " " << rec[i][j]; cout << endl; } } return 0; }
最小割:
裸最小割:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, s, t, x;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; } inline bool Bfs(){ for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[t] != -1; } inline int dfs(int u, int flow){ if(u == t) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 1; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(s, inf); } return ans; } map<pair<int, int>, int> wei; vector<int> G[maxn]; int vis[maxn]; void DFS(int st) { vis[st] = 1; for (int i=0; i < G[st].size(); ++ i) { int v = G[st][i]; if (vis[v]) continue; Add_Edge(st, v, wei[{st, v}]), Add_Edge(v, st, 0); //cout << st << " ---- " << v << " " << wei[{st, v}] << endl; if (G[v].size() == 1) { //cout << v << " ---- " << t << endl; Add_Edge(v, t, inf), Add_Edge(t, v, 0); } else DFS(v); } } int main(){ init(); int root; scanf("%d %d", &n, &root); s = 1, t = n+2; node_num = n+2; Add_Edge(s, root+1, inf), Add_Edge(root+1, s, 0); for (int i = 1; i <= n-1; i++) { int u, v, w; scanf("%d %d %d", &u, &v, &w); u ++, v ++; wei[{u, v}] = w, wei[{v, u}] = w; G[u].push_back(v), G[v].push_back(u); } DFS(root+1); printf("%d\n", Dinic()); return 0; }
P2057 [SHOI2007]善意的投票 / [JLOI2010]冠军调查
这道题建双向边这里挺耐人寻味的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, s, t, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; } inline bool Bfs(){ for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[t] != -1; } inline int dfs(int u, int flow){ if(u == t) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 1; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(s, inf); } return ans; } int main(){ cnt = 0; memset(head, -1, sizeof(head)); scanf("%d %d", &n, &m); //书,练习册,答案 s = 1, t = n+2; node_num = t; for (int i = 1,temp; i <= n; ++ i) { scanf("%d", &temp); if (temp) Add_Edge(s, i+1, 1), Add_Edge(i+1, s, 0); else Add_Edge(i+1, t, 1), Add_Edge(t, i+1, 0); } for (int i = 1, u, v; i <= m; i++) { scanf("%d %d", &u, &v); Add_Edge(u+1, v+1, 1), Add_Edge(v+1, u+1, 1), Add_Edge(u+1, v+1, 0), Add_Edge(v+1, u+1, 0); } printf("%d\n", Dinic()); return 0; }
P1344 [USACO4.4]追查坏牛奶Pollutant Control
这题有一个如何统计割边数的小技巧:
就是给边权加上一个大数+1之后再模这个大数,剩下的就是割边数了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll maxn = 1e6 + 10; const ll maxm = 1e5 * 2 + 10; const ll inf = 0x3f3f3f3f; const ll mod = 1e9 + 7; struct Edge{ ll to; ll next; ll w; }edge[maxn*2]; ll head[maxn]; ll cur[maxn];//当前弧优化数组 ll n, m, s, t, k;//点数,边数,源点,汇点 ll dis[maxn];//Bfs深度 ll cnt = 0;//边数 ll node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline void Add_Edge(ll u, ll v, ll w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; } inline bool Bfs(){ for(ll i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[s] = 0; queue<ll> q; q.push(s); while(!q.empty()){ ll u = q.front(); q.pop(); for(ll i = head[u]; i != -1; i = edge[i].next){ ll v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[t] != -1; } inline ll dfs(ll u, ll flow){ if(u == t) return flow; ll del = flow; for(ll i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 ll v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 ll ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline ll Dinic(){ ll ans = 0; while(Bfs()){ for(ll i = 1; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(s, inf); } return ans; } int main(){ ll cnt = 0; memset(head, -1, sizeof(head)); scanf("%lld %lld", &n, &m); s = 1, t = n; node_num = t; for (ll i = 1, u, v, w; i <= m; ++ i) { scanf("%lld %lld %lld", &u, &v, &w); w = w * 1050 + 1; Add_Edge(u, v, w), Add_Edge(v, u, 0); } ll temp = Dinic(); printf("%lld %lld\n", temp/1050, temp % 1050); return 0; }
P2944 [USACO09MAR]Earthquake Damage 2 G
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, S, T, p;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } int dfs(int u, int flow){ if(u == T) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } int main() { init(); scanf("%d %d %d", &n, &m, &p); S = 1, T = 2*n+1; node_num = T; Add_Edge(S, S+1, inf); for (int i = 1, u, v; i <= m; ++ i) { scanf("%d %d", &u, &v); Add_Edge(u*2, v*2-1, inf); Add_Edge(v*2, u*2-1, inf); } int rec[n+1] = {0}; for (int i = 1, pi; i <= p; ++ i) { scanf("%d", &pi); Add_Edge(pi*2-1, pi*2, inf); Add_Edge(pi*2, T, inf); rec[pi] = 1; } for (int i = 2; i <= n; ++ i) { if (!rec[i]) Add_Edge(i*2-1, i*2, 1); } printf("%d\n", Dinic()); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, S, T, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; int rec[100+5][100+5]; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline int dfs(int u, int flow){ if(u == T) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } bool judge(int mid) { init(); for (int i = 1; i <= n; ++ i) { Add_Edge(S, i, mid); Add_Edge(i, i+n, k); for (int j = 1; j <= n; ++j) { if (rec[i][j]) Add_Edge(i, j+2*n, 1); else Add_Edge(i+n, j+3*n, 1); } } for (int i = 1; i <= n; ++ i) { Add_Edge(i+3*n,i+2*n, k); Add_Edge(i+2*n, T, mid); } return Dinic() == mid * n; } char g[105][105]; int main() { int a1, a2, an, b1, b2, bn; while (~scanf("%d %d %d %d %d %d %d %d", &n, &a1, &a2, &an, &b1, &b2, &bn)) { init(); ++ a1, ++ a2, ++ b1, ++ b2; S = 0, T = n+1; node_num = T; Add_Edge(S, a1, inf), Add_Edge(S, b1, inf); Add_Edge(a2, T, inf), Add_Edge(b2, T, inf); for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= n; ++ j) { cin >> g[i][j]; if (g[i][j] == 'X') continue; else if(g[i][j] == 'O') Add_Edge(i, j, 2); else Add_Edge(i, j, inf); } } int flag = 0; if (Dinic() >= 2*(an+bn)) flag = 1; if (flag) { init(); Add_Edge(S, a1, inf), Add_Edge(S, b2, inf); Add_Edge(a2, T, inf), Add_Edge(b1, T, inf); for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= n; ++ j) { if (g[i][j] == 'X') continue; else if(g[i][j] == 'O') Add_Edge(i, j, 2); else Add_Edge(i, j, inf); } } if (Dinic() >= 2*(an+bn)) cout << "Yes" << endl; else cout << "No" << endl; } else cout << "No" << endl; } return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; #define pii pair<long long, int> typedef long long ll; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const ll inf = 1e18; const int mod = 1e9 + 7; struct Edge{ int to; int next; ll w; } edge[maxn*2]; int head[maxn], node_num; int cur[maxn];//当前弧优化数组 int n, m, S, T, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 bool vis[maxn]; inline void init(){ cnt = 0; for (int i = 0; i <= node_num; ++ i) head[i] = -1; } inline void Add_Edge(int u, int v, ll w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline ll dfs(int u, ll flow){ if(u == T) return flow; ll del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 ll ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline ll Dinic() { ll ans = 0; while(Bfs()) { for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } int dic[4][2] = {1,0, -1,0, 0,-1, 0,1}; int main() { scanf("%d %d", &n, &m); S = 0, T = 2*n*m+1; node_num = T; init(); int sum = 0; int g[n+1][m+1]; for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= m; ++ j) { scanf("%d", &g[i][j]); int id = (i-1)*m+j, id1 = id+n*m; if (g[i][j] > 0) Add_Edge(id, id1, g[i][j]); else if (g[i][j] == 0) Add_Edge(S, id, inf), Add_Edge(id, id1, inf); } } for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= m; ++ j) { if (g[i][j] == -1) continue; for (int k = 0; k < 4; ++ k) { int tx = i + dic[k][0], ty = j + dic[k][1]; int id = (i-1)*m+j, id1 = id+n*m; int td = (tx-1)*m+ty, td1 = (tx-1)*m+ty+n*m; if (tx < 1 || ty < 1 || tx > n || ty > m) { Add_Edge(id1, T, inf); continue; } if (g[tx][ty] == -1) continue; Add_Edge(id1, td, inf); } } } cout << Dinic() << endl; return 0; }
这题是我用ISAP卡过去的,正解应该是对偶图最短路等于最小割
UPD20.09.23: 已在对偶图专题更新。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e7 + 10; //const int maxm = 1e3 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; int m,n,S,T,k, x, head[maxn], num_edge; int cur[maxn],deep[maxn],last[maxn],num[maxn]; int node_num; int cap[40]; int wanted[maxn][40]; //cur当前弧优化; last该点的上一条边; num桶 用来GAP优化 struct Edge{ int next,to,dis; }edge[maxn*2]; void add_edge(int from,int to,int dis) { edge[num_edge].to=to; edge[num_edge].dis=dis; edge[num_edge].next=head[from]; head[from]=num_edge++; // // edge[num_edge].to=from; // edge[num_edge].dis=0; // edge[num_edge].next=head[to]; // head[to]=num_edge++; } //bfs仅用于更新deep void bfs(int T) { queue<int>q; for (int i=0; i<=node_num; i++) cur[i]=head[i]; for (int i=0; i<=node_num; i++) deep[i]=node_num; deep[T]=0; q.push(T); while (!q.empty()) { int now=q.front(); q.pop(); for (int i=head[now]; i != -1; i=edge[i].next) { if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边 { deep[edge[i].to] = deep[now]+1; q.push(edge[i].to); } } } } int add_flow(int S,int T) { int ans=inf,now=T; while (now!=S) { ans=min(ans,edge[last[now]].dis); now=edge[last[now]^1].to; } now=T; while (now!=S) { edge[last[now]].dis-=ans; edge[last[now]^1].dis+=ans; now=edge[last[now]^1].to; } return ans; } int isap() { int now=S; int maxflow = 0; bfs(T);//搜出一条增广路 for (int i=1; i<=node_num; i++) num[deep[i]]++; while (deep[S]<node_num) { if (now==T) {//如果到达汇点就直接增广,重新回到源点进行下一轮增广 maxflow+=add_flow(S,T); now=S;//回到源点 } bool has_find=0; for (int i=cur[now]; i!=-1; i=edge[i].next) { if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路 { has_find=true; cur[now]=i;//当前弧优化 now=edge[i].to; last[edge[i].to]=i; break; } } if (!has_find)//没有找到出边,重新编号 { int minn=node_num-1; for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径 if (edge[i].dis) minn=min(minn,deep[edge[i].to]); if ((--num[deep[now]])==0) break;//GAP优化 出现了断层 num[deep[now]=minn+1]++; cur[now]=head[now]; if (now!=S) now=edge[last[now]^1].to; } } return maxflow; } void init() { num_edge = 0; memset(head,-1,sizeof(head)); } 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 main() { n = read(), m = read(); init(); S = 1, T = n*m; node_num = T; for (int i = 1; i <= n; ++ i) { int w; for (int j = 2; j <= m; ++ j) { w = read(); int id1 = (i-1)*m+j-1, id2 = (i-1)*m+j; add_edge(id1, id2, w); add_edge(id2, id1, w); } } for (int i = 2; i <= n; ++ i) { int w; for (int j = 1; j <= m; ++ j) { w = read(); int id1 = (i-2)*m+j, id2 = (i-1)*m+j; add_edge(id1, id2, w); add_edge(id2, id1, w); } } for (int i = 2; i <= n; ++ i) { int w; for (int j = 2; j <= m; ++ j) { w = read(); int id1 = (i-2)*m+j-1, id2 = (i-1)*m+j; add_edge(id1, id2, w); add_edge(id2, id1, w); } } cout << isap() << endl; return 0; }
割点转割边:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; //const int maxm = 1e3 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; int m,n,s,t, x, head[maxn], num_edge; int cur[maxn],deep[maxn],last[maxn],num[maxn]; int node_num; //cur当前弧优化; last该点的上一条边; num桶 用来GAP优化 struct Edge{ int next,to,dis; }edge[maxn*2]; void add_edge(int from,int to,int dis) { edge[num_edge].to=to; edge[num_edge].dis=dis; edge[num_edge].next=head[from]; head[from]=num_edge++; } //bfs仅用于更新deep void bfs(int t) { queue<int>q; for (int i=0; i<=node_num; i++) cur[i]=head[i]; for (int i=1; i<=node_num; i++) deep[i]=node_num; deep[t]=0; q.push(t); while (!q.empty()) { int now=q.front(); q.pop(); for (int i=head[now]; i != -1; i=edge[i].next) { if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边 { deep[edge[i].to] = deep[now]+1; q.push(edge[i].to); } } } } int add_flow(int s,int t) { int ans=inf,now=t; while (now!=s) { ans=min(ans,edge[last[now]].dis); now=edge[last[now]^1].to; } now=t; while (now!=s) { edge[last[now]].dis-=ans; edge[last[now]^1].dis+=ans; now=edge[last[now]^1].to; } return ans; } int isap(int s,int t) { int now=s; int maxflow = 0; bfs(t);//搜出一条增广路 for (int i=1; i<=node_num; i++) num[deep[i]]++; while (deep[s]<node_num) { if (now==t) {//如果到达汇点就直接增广,重新回到源点进行下一轮增广 maxflow+=add_flow(s,t); now=s;//回到源点 } bool has_find=0; for (int i=cur[now]; i!=-1; i=edge[i].next) { if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路 { has_find=true; cur[now]=i;//当前弧优化 now=edge[i].to; last[edge[i].to]=i; break; } } if (!has_find)//没有找到出边,重新编号 { int minn=node_num-1; for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径 if (edge[i].dis) minn=min(minn,deep[edge[i].to]); if ((--num[deep[now]])==0) break;//GAP优化 出现了断层 num[deep[now]=minn+1]++; cur[now]=head[now]; if (now!=s) now=edge[last[now]^1].to; } } return maxflow; } int main(){ num_edge = 0; memset(head,-1,sizeof(head)); scanf("%d%d%d%d",&n,&m,&s,&t); node_num = 2*n; for (int i = 1; i <= n; ++ i) add_edge(i,i+n,1), add_edge(i+n,i,0); for(int i=0;i<m;i++){ int u,v,cap; scanf("%d%d",&u,&v); add_edge(u+n,v,inf), add_edge(v,u+n,0); add_edge(v+n,u,inf), add_edge(u,v+n,0); } int ans = isap(s+n,t); printf("%d\n", ans); return 0; }
最大权闭合子图:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; //const int maxm = 1e3 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; int m,n,s,t, x, head[maxn], num_edge; int cur[maxn],deep[maxn],last[maxn],num[maxn]; int node_num; //cur当前弧优化; last该点的上一条边; num桶 用来GAP优化 struct Edge{ int next,to,dis; }edge[maxn*2]; void add_edge(int from,int to,int dis) { edge[num_edge].to=to; edge[num_edge].dis=dis; edge[num_edge].next=head[from]; head[from]=num_edge++; } //bfs仅用于更新deep void bfs(int t) { queue<int>q; for (int i=0; i<=node_num; i++) cur[i]=head[i]; for (int i=1; i<=node_num; i++) deep[i]=node_num; deep[t]=0; q.push(t); while (!q.empty()) { int now=q.front(); q.pop(); for (int i=head[now]; i != -1; i=edge[i].next) { if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边 { deep[edge[i].to] = deep[now]+1; q.push(edge[i].to); } } } } int add_flow(int s,int t) { int ans=inf,now=t; while (now!=s) { ans=min(ans,edge[last[now]].dis); now=edge[last[now]^1].to; } now=t; while (now!=s) { edge[last[now]].dis-=ans; edge[last[now]^1].dis+=ans; now=edge[last[now]^1].to; } return ans; } int isap(int s,int t) { int now=s; int maxflow = 0; bfs(t);//搜出一条增广路 for (int i=1; i<=node_num; i++) num[deep[i]]++; while (deep[s]<node_num) { if (now==t) {//如果到达汇点就直接增广,重新回到源点进行下一轮增广 maxflow+=add_flow(s,t); now=s;//回到源点 } bool has_find=0; for (int i=cur[now]; i!=-1; i=edge[i].next) { if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路 { has_find=true; cur[now]=i;//当前弧优化 now=edge[i].to; last[edge[i].to]=i; break; } } if (!has_find)//没有找到出边,重新编号 { int minn=node_num-1; for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径 if (edge[i].dis) minn=min(minn,deep[edge[i].to]); if ((--num[deep[now]])==0) break;//GAP优化 出现了断层 num[deep[now]=minn+1]++; cur[now]=head[now]; if (now!=s) now=edge[last[now]^1].to; } } return maxflow; } int main(){ num_edge = 0; memset(head,-1,sizeof(head)); scanf("%d",&n); int cap1[n+2], cap2[n+2]; int sum = 0; for (int i = 2; i < n+2; i++) scanf("%d", &cap1[i]), sum += cap1[i]; for (int i = 2; i < n+2; i++) scanf("%d", &cap2[i]), sum += cap2[i]; scanf("%d", &m); for (int i = 2; i < n+2; ++ i) { add_edge(1, i, cap1[i]), add_edge(i, 1, 0); add_edge(i, n+2*m+2, cap2[i]), add_edge(n+2*m+2, i, 0); } node_num = n+2*m+2; s = 1, t = n+2*m+2; for (int i = 1, ki, c1, c2; i <= m; ++ i) { scanf("%d%d%d", &ki, &c1, &c2); sum += c1 + c2; int add1 = (n+1)+(i-1)*2+1, add2 = (n+1)+(i-1)*2+2; add_edge(s, add1, c1), add_edge(add1, s, 0); add_edge(add2, t, c2), add_edge(t, add2, 0); for (int j = 1, tp; j <= ki; ++ j) { scanf("%d", &tp); tp += 1; add_edge(add1, tp, inf), add_edge(tp, add1, 0); add_edge(tp, add2, inf), add_edge(add2, tp, 0); } } int ans = sum - isap(s,t); printf("%d\n", ans); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, S, T, k, d;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline int dfs(int u, int flow){ if(u == T) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } int main() { init(); m = read(), n = read(); S = 0, T = n+m+1; node_num = T; int sum = 0; for (int i = 1, w; i <= m; ++ i) { scanf("%d", &w), sum += w; Add_Edge(S, i, w); while(getchar() == ' ') { scanf("%d", &w); Add_Edge(i, m+w,inf); } } for (int i = 1, w; i <= n; ++ i) { w = read(); Add_Edge(i+m, T, w); } sum -= Dinic(); for (int i = 1; i <= m; i++) if (~dis[i]) printf("%d ",i); printf("\n"); for (int i = 1; i <= n; i++) if (~dis[m+i]) printf("%d ", i); printf("\n%d\n", sum); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; #define pii pair<long long, int> typedef long long ll; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const ll inf = 1e18; const int mod = 1e9 + 7; struct Edge{ int to; int next; ll w; } edge[maxn*2]; int head[maxn], node_num; int cur[maxn];//当前弧优化数组 int n, m, S, T, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 bool vis[maxn]; inline void init(){ cnt = 0; for (int i = 0; i <= node_num; ++ i) head[i] = -1; } inline void Add_Edge(int u, int v, ll w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline ll dfs(int u, ll flow){ if(u == T) return flow; ll del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 ll ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline ll Dinic() { ll ans = 0; while(Bfs()) { for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } int main() { scanf("%d %d", &m, &n); S = 0, T = n+m+1; node_num = T; init(); int sum = 0; for (int i = 1, temp; i <= m; ++ i) { int cost; scanf("%d", &cost), sum += cost; Add_Edge(i+n, T, cost); while (scanf("%d", &temp) && temp) Add_Edge(temp, i+n, inf); } for (int i = 1, cost; i <= n; ++ i) scanf("%d", &cost), Add_Edge(S, i, cost); printf("%d\n", sum-Dinic()); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; #define pii pair<long long, int> typedef long long ll; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const ll inf = 1e18; const int mod = 1e9 + 7; struct Edge{ int to; int next; ll w; } edge[maxn*2]; int head[maxn], node_num; int cur[maxn];//当前弧优化数组 int n, m, S, T, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 bool vis[maxn]; inline void init(){ cnt = 0; memset(head, -1, sizeof(head)); } inline void Add_Edge(int u, int v, ll w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline ll dfs(int u, ll flow){ if(u == T) return flow; ll del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 ll ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline ll Dinic() { ll ans = 0; while(Bfs()) { for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } int main() { scanf("%d %d", &n, &m); S = 0, T = n+1; node_num = T; init(); int sum = 0; for (int i = 1, w; i <= n; ++ i) { scanf("%d", &w); if (w > 0) Add_Edge(S, i, w), sum += w; else Add_Edge(i, T, -w); } for (int i = 1,u,v,w; i <= m; ++ i) { scanf("%d %d %d", &u, &v, &w); Add_Edge(u, v, w); } cout << sum - Dinic() << endl; return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; #define pii pair<int, int> typedef long long ll; const int maxn = 2e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; } edge[maxn*2]; int head[maxn], node_num; int cur[maxn];//当前弧优化数组 int n, m, S, T, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 inline void init(){ cnt = 0; memset(head, -1, sizeof(head)); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline int dfs(int u, int flow){ if(u == T) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic() { int ans = 0; while(Bfs()) { for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } 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 main() { n = read(), m = read(); S = 0, T = n+m+1; node_num = T; init(); int sum = 0; for (int i = 1, ci; i <= n; ++ i) { ci = read(); Add_Edge(m+i, T, ci); } for (int i = 1,u,v,w; i <= m; ++ i) { u = read(), v = read(), w = read(); Add_Edge(S, i, w); sum += w; Add_Edge(i, u+m, inf); Add_Edge(i, v+m, inf); } cout << sum - Dinic() << endl; return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; #define pii pair<int, int> typedef long long ll; const int maxn = 2e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; } edge[maxn*2]; int head[maxn], node_num; int cur[maxn];//当前弧优化数组 int n, m, S, T, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 inline void init(){ cnt = 0; memset(head, -1, sizeof(head)); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline int dfs(int u, int flow){ if(u == T) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic() { int ans = 0; while(Bfs()) { for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } 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 dic[5][2] = {1,0,-1,0,0,-1,0,1,0,0}; int main() { n=read(),m=read(); S = 0, T = 3*n*m+1; node_num =3*n*m+1; int sum = 0; init(); for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) { int tmp=read(); Add_Edge(S,(i-1)*m+j,tmp); sum+=tmp; } } for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) { int tmp=read(); Add_Edge((i-1)*m+j,T,tmp); sum+=tmp; } } for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) { int tmp=read(); Add_Edge(S,n*m+(i-1)*m+j,tmp); sum+=tmp; } } for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) { int tmp=read(); Add_Edge(2*n*m+(i-1)*m+j,T,tmp); sum+=tmp; } } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { for (int k=0;k<5;k++) { int nx=i+dic[k][0],ny=j+dic[k][1]; if (nx<1 || nx>n || ny<1 || ny>m) continue; Add_Edge(n*m+(nx-1)*m+ny,(i-1)*m+j,inf); Add_Edge((i-1)*m+j,2*n*m+(nx-1)*m+ny,inf); } } cout << sum-Dinic() << endl; return 0; }
较为经典的最小割:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, S, T;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline int dfs(int u, int flow){ if(u == T) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } int g[205][205]; int dic[4][2] = {1,0, -1,0, 0,-1, 0,1}; int main() { init(); scanf("%d %d", &n, &m); S = 0, T = n*m+1; node_num = T; int sum = 0; for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j) scanf("%d", &g[i][j]), sum += g[i][j]; for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= m; ++ j) { int id = (i-1)*m+j; if ((i+j) % 2 == 0) { Add_Edge(S, id, g[i][j]); for (int k = 0; k < 4; ++ k) { int tx = i + dic[k][0], ty = j + dic[k][1]; int id1 = (tx-1)*m+ty; if (tx < 1 || ty < 1 || tx > n || ty > m) continue; Add_Edge(id, id1, inf); } } else Add_Edge(id, T, g[i][j]); } } printf("%d\n",sum - Dinic()); return 0; }
二分图网络流(Dinic跟二分图跟配哦):
最大匹配:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, s, t, x;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; } inline bool Bfs(){ for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[t] != -1; } inline int dfs(int u, int flow){ if(u == t) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 1; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(s, inf); } return ans; } int main(){ init(); scanf("%d",&n); node_num = 2+n*3; s = 1, t = 2+n*3; for (int i = 1, a, b; i <= n*2; ++ i) { scanf("%d %d", &a, &b); a += 1+n*2, b += 1+n*2; Add_Edge(s, i+1, 1), Add_Edge(i+1, s, 0); Add_Edge(i+1, a, 1), Add_Edge(a, i+1, 0); Add_Edge(i+1, b, 1), Add_Edge(b, i+1, 0); } for (int i = 1; i <= n; ++ i) Add_Edge(i+2*n+1, t, 2), Add_Edge(t, i+2*n+1, 0); int ans = Dinic(); printf("%d\n", ans); return 0; }
CF387D George and Interesting Graph
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e4 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, s, t, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; } inline bool Bfs(){ for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[t] != -1; } inline int dfs(int u, int flow){ if(u == t) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 1; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(s, inf); } return ans; } int main(){ scanf("%d %d", &n, &m); s = 1, t = n*2+2; node_num = n*2+2; int ans = 1000000; int in[n+1] = {0}, out[n+1] = {0}; pair<int, int> rec[maxn]; for (int i = 1, u, v; i <= m; i++) { scanf("%d %d", &u, &v); ++out[u], ++in[v]; if (u == v) in[v]--; rec[i] = {u, v}; } for (int i = 1 ; i <= n; ++ i) { init(); int temp = in[i]+out[i]; for (int j = 1; j <= n; ++ j) Add_Edge(s,j+1,1), Add_Edge(j+1,s,0), Add_Edge(j+n+1,t,1), Add_Edge(t,j+n+1,0); for (int j = 1; j <= m; ++ j) { if (rec[j].first == i || rec[j].second == i) continue; Add_Edge(rec[j].first+1, rec[j].second+1+n, 1), Add_Edge(rec[j].second+1+n, rec[j].first+1, 0); } ans = min(ans, n-1-2*Dinic()+m+2*n-1-2*temp); } printf("%d\n", ans); return 0; }
这一题输出匹配的方式挺有用的。边权为0的边代表是最大流边
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e7 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, s, t, k, r, d;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[t] != -1; } inline int dfs(int u, int flow){ if(u == t) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(s, inf); } return ans; } double cal(int x1, int y1, int x2, int y2) { return sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); } int main() { cnt = 0; memset(head, -1, sizeof(head)); scanf("%d%d", &n, &m); s = 1, t = n+m+2; node_num = t; pair<int, int> rec_n[n+1], rec_m[m+1]; for(int i = 1, u, v; i <= n; i ++) scanf("%d %d", &rec_n[i].first, &rec_n[i].second); for(int i = 1, u, v; i <= m; i ++) scanf("%d %d", &rec_m[i].first, &rec_m[i].second); for (int i = 2; i <= n ; ++ i) { for (int j = 1; j <= m; ++ j) { int u1 = rec_n[i-1].first, v1 = rec_n[i-1].second, u2 = rec_n[i].first, v2 = rec_n[i].second, u3 = rec_m[j].first, v3 = rec_m[j].second; if (2*cal(u1, v1, u2, v2) > (cal(u1, v1, u3, v3)+ cal(u2, v2, u3, v3))) Add_Edge(i+1, j+n+1, 1); } } for (int i = 2; i <= n ; ++ i) Add_Edge(s, i+1, 1); for (int i = 1; i <= m; ++ i) Add_Edge(i+n+1, t, 1); printf("%d\n%d %d ", n+Dinic(), rec_n[1].first, rec_n[1].second); for (int i = 2; i <= n; i++) { for (int j = head[i+1]; ~j; j = edge[j].next) { int v = edge[j].to; if (v == s || v == t || edge[j].w) continue; printf("%d %d ", rec_m[v-n-1].first, rec_m[v-n-1].second); } printf("%d %d ", rec_n[i].first, rec_n[i].second); } return 0; }
最大独立集:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, s, t, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; } inline bool Bfs(){ for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[t] != -1; } inline int dfs(int u, int flow){ if(u == t) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 1; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(s, inf); } return ans; } int g[205][205]; int dic[8][2] = {1,3, -1,3, 1,-3, -1,-3, 3,1, 3,-1, -3,1, -3,-1}; bool check(int tx, int ty) { return (tx < 1 || ty < 1 || ty > m || tx > n || g[tx][ty]); } int main(){ init(); scanf("%d %d %d", &n, &m, &k); s = 1, t = n*m+2; node_num = n*m+2; int ans = 0; for (int i = 1, u, v; i <= k; i++) scanf("%d %d", &u, &v), ans -= g[u][v]&1, g[u][v]= 1; for (int i = 1 ; i <= n; ++ i) { for (int j = 1; j <= m; ++ j) { if (g[i][j]) continue; int id1 = (i-1)*m+1+j; if (i & 1) { Add_Edge(s, id1, 1), Add_Edge(id1, s, 0); for (int k = 0; k < 8; ++ k) { int tx = i + dic[k][0], ty = j + dic[k][1]; int id2 = (tx-1)*m+ty+1; if (!check(tx, ty)) Add_Edge(id1, id2, 1), Add_Edge(id2, id1, 0); } } else Add_Edge(id1, t, 1), Add_Edge(t, id1, 0); } } printf("%d\n", n*m-ans-Dinic()-k); return 0; }
P3033 [USACO11NOV]Cow Steeplechase G
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, S, T, p;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline int dfs(int u, int flow){ if(u == T) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } struct LINE { int x1,y1, x2,y2; }; bool judge(LINE a, LINE b) { return b.x1<=a.x1&&a.x1<=b.x2&&a.y1<=b.y1&&b.y1<=a.y2; } int main() { init(); scanf("%d", &n); S = 0, T = n+1; node_num = T; LINE A[2*n+1], B[2*n+1]; int ca = 0, cb = 0; for (int i = 1, x1,x2,y1,y2; i <= n; ++ i) { scanf("%d %d %d %d", &x1, &y1, &x2, &y2); if (x1 > x2) swap(x1, x2); if (y1 > y2) swap(y1, y2); if (x1 == x2) A[++ca] = {x1,y1,x2,y2}; else B[++cb] = {x1,y1,x2,y2}; } for (int i = 1; i <= ca; ++ i) Add_Edge(S, i, 1); for (int i = 1; i <= cb; ++ i) Add_Edge(i+ca, T, 1); for (int i = 1; i <= ca; ++ i) for (int j = 1; j <= cb; ++ j) if (judge(A[i], B[j])) Add_Edge(i, ca+j, 1); printf("%d\n", n - Dinic()); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn = 300+10; const int maxm = 100000+100; const int inf = 0x3f3f3f3f; int n, m, r, c; int tot, head[maxm]; int ans, linkx[maxm], linky[maxm]; int dx[maxm], dy[maxm]; char g[maxn][maxn]; int id[maxn][maxn]; int dir[8][2]={-1,2,-1,-2, 1,2,1,-2, 2,1, 2,-1, -2,1,-2,-1}; struct E { int to, next; }edge[maxm]; //这里千万要注意,如果题目是双向边的话,这里的N要开2*N inline void read(int& ans) { 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(); } ans = x * f; } inline void add_edge(int u, int v) { edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; if (!linkx[u] && !linky[v]) linky[v] = u, linkx[u] = v, ++ ans; } void init() { tot = ans = 0; memset(head, -1, sizeof(head)); memset(linkx, 0, sizeof(linkx)); memset(linky, 0, sizeof(linky)); } bool bfs() { bool ret = 0; queue<int> q; memset(dx, 0, sizeof(dx)), memset(dy, 0, sizeof(dy)); for (int i = 1; i <= n*n; ++ i) if (!linkx[i]) q.push(i); while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; if (!dy[v]) { dy[v] = dx[u] + 1; if (!linky[v]) ret = 1; else dx[linky[v]] = dy[v]+1, q.push(linky[v]); } } } return ret; } bool dfs(int u) { for (int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; if (dy[v] == dx[u] + 1) { dy[v] = 0; if (!linky[v] || dfs(linky[v])) { linky[v] = u, linkx[u] = v; return 1; } } } return 0; } int main() { init(); ios::sync_with_stdio(0); cin.tie(0); cin >> n; for (int i = 1; i <= n; ++ i) for (int j = 1; j <= n; ++ j) cin >> g[i][j], id[i][j] = (i-1)*n+j; int res = 0; for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= n; ++ j) { if (g[i][j] == '0') { ++ res; for (int k = 0; k < 8; ++ k) { int tx = i + dir[k][0], ty = j+ dir[k][1]; if (ty < 1 || ty > n || tx < 1 || tx > n || g[tx][ty] == '1') continue; add_edge(id[i][j], id[tx][ty]); } } } } while (bfs()) { for (int i = 1; i <= n*n; ++ i) if (!linkx[i] && dfs(i)) ++ ans; } cout << res-ans/2 << endl; //这段循环是用来输出跟谁匹配 // printf("%d\n", ans); // for (int i = 1; i <= n; ++ i) // if (linkx[i]) printf("%d %d\n",i, linkx[i]); return 0; }
最小路径覆盖:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, s, t, k, r, c;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; } inline bool Bfs(){ for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[s] = 0; queue<int> q; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[t] != -1; } inline int dfs(int u, int flow){ if(u == t) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 1; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(s, inf); } return ans; } char g[205][205]; int id[205][205]; int dir[2][2]={1,-1,1,1}; int main(){ cnt = 0; memset(head, -1, sizeof(head)); scanf("%d %d %d %d", &n, &m, &r, &c); s = 1, t = n*m*2+2; node_num = t; for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j) cin >> g[i][j], id[i][j] = (i-1)*m+j+1; int res = 0; for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= m; ++ j) { Add_Edge(s, id[i][j], 1), Add_Edge(id[i][j], s, 0); Add_Edge(id[i][j] + n*m, t, 1), Add_Edge(t, id[i][j] + n*m, 0); if (g[i][j] == '.') { ++ res; for (int k = 0; k < 2; ++ k) { int tx = i + r*dir[k][0], ty = j+ c*dir[k][1]; if (ty < 1 || ty > m || tx < 1 || tx > n || g[tx][ty] == 'x') continue; Add_Edge(id[i][j], id[tx][ty]+n*m, 1), Add_Edge(id[tx][ty]+n*m, id[i][j], 0); } for (int k = 0; k < 2; ++ k) { int tx = i + c*dir[k][0], ty = j + r*dir[k][1]; if (ty < 1 || ty > m || tx < 1 || tx > n || g[tx][ty] == 'x') continue; Add_Edge(id[i][j], id[tx][ty]+n*m, 1), Add_Edge(id[tx][ty]+n*m, id[i][j], 0); } } } } printf("%d\n", res-Dinic()); return 0; }
ha之前用二分图刷了就没用dinic了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn = 501; const int maxm = 250001; const int inf = 0x3f3f3f3f; int n, m; int tot, head[maxn]; int ans, linkx[maxn], linky[maxn]; int dx[maxn], dy[maxn]; struct E { int to, next; }edge[maxm]; //这里千万要注意,如果题目是双向边的话,这里的N要开2*N inline void read(int& ans) { 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(); } ans = x * f; } void inti() { } //加边 inline void add_edge(int u, int v) { edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; if (!linkx[u] && !linky[v]) linky[v] = u, linkx[u] = v, ++ ans; } bool bfs() { bool ret=0; queue<int> q; memset(dx, 0, sizeof(dx)), memset(dy, 0, sizeof(dy)); for (int i = 1; i <= n; ++ i) if (!linkx[i]) q.push(i); while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; if (!dy[v]) { dy[v] = dx[u] + 1; if (!linky[v]) ret = 1; else dx[linky[v]] = dy[v]+1, q.push(linky[v]); } } } return ret; } bool dfs(int u) { for (int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; if (dy[v] == dx[u] + 1) { dy[v] = 0; if (!linky[v] || dfs(linky[v])) { linky[v] = u, linkx[u] = v; return 1; } } } return 0; } int main() { int a, b; memset(head, -1, sizeof(head)); read(n), read(m); for (int i = 1; i <= m; ++ i) read(a), read(b), add_edge(a,b); while(bfs()) { for (int i = 1; i <= n; ++ i) if (!linkx[i] && dfs(i)) ++ ans; } bool vis[n+1] = {0}; stack<int> s; for (int i = 1; i <= n; ++ i) { if (vis[i]) continue; int cnt = 0; int t = i; int res[n+1]; vis[t] = 1; s.push(t); while (linky[t]) { t = linky[t]; s.push(t); vis[t] = 1; } while (!s.empty()) { res[cnt++] = s.top(); s.pop(); } t = i; while (linkx[t]) { t = linkx[t]; res[cnt++] = t; vis[t] = 1; } for (int i = 0; i < cnt-1; ++ i) cout << res[i] << " "; cout << res[cnt-1] << endl; } printf("%d\n",n-ans); // 这段循环是用来输出跟谁匹配 // for (int i = 1; i <= n; ++ i) // printf("%d ",linkx[i]); return 0; }
二分答案 + 网络流check
P2857 [USACO06FEB]Steady Cow Assignment G
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; //const int maxm = 1e3 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; int m,n,s,t, x, head[maxn], num_edge; int cur[maxn],deep[maxn],last[maxn],num[maxn]; int node_num; int cap[40]; int wanted[maxn][40]; //cur当前弧优化; last该点的上一条边; num桶 用来GAP优化 struct Edge{ int next,to,dis; }edge[maxn*2]; void add_edge(int from,int to,int dis) { edge[num_edge].to=to; edge[num_edge].dis=dis; edge[num_edge].next=head[from]; head[from]=num_edge++; } //bfs仅用于更新deep void bfs(int t) { queue<int>q; for (int i=0; i<=node_num; i++) cur[i]=head[i]; for (int i=1; i<=node_num; i++) deep[i]=node_num; deep[t]=0; q.push(t); while (!q.empty()) { int now=q.front(); q.pop(); for (int i=head[now]; i != -1; i=edge[i].next) { if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边 { deep[edge[i].to] = deep[now]+1; q.push(edge[i].to); } } } } int add_flow(int s,int t) { int ans=inf,now=t; while (now!=s) { ans=min(ans,edge[last[now]].dis); now=edge[last[now]^1].to; } now=t; while (now!=s) { edge[last[now]].dis-=ans; edge[last[now]^1].dis+=ans; now=edge[last[now]^1].to; } return ans; } int isap(int s,int t) { int now=s; int maxflow = 0; bfs(t);//搜出一条增广路 for (int i=1; i<=node_num; i++) num[deep[i]]++; while (deep[s]<node_num) { if (now==t) {//如果到达汇点就直接增广,重新回到源点进行下一轮增广 maxflow+=add_flow(s,t); now=s;//回到源点 } bool has_find=0; for (int i=cur[now]; i!=-1; i=edge[i].next) { if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路 { has_find=true; cur[now]=i;//当前弧优化 now=edge[i].to; last[edge[i].to]=i; break; } } if (!has_find)//没有找到出边,重新编号 { int minn=node_num-1; for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径 if (edge[i].dis) minn=min(minn,deep[edge[i].to]); if ((--num[deep[now]])==0) break;//GAP优化 出现了断层 num[deep[now]=minn+1]++; cur[now]=head[now]; if (now!=s) now=edge[last[now]^1].to; } } return maxflow; } bool check(int range) { for (int i = 1; i + range - 1 <= m; ++ i) { num_edge = 0; memset(head,-1,sizeof(head)); for (int j = 1; j <= m; ++ j) { int barn = n+1+j; add_edge(barn, t, cap[j]), add_edge(t, barn, 0); } for (int j = 2; j <= n+1; ++ j) { add_edge(s, j, 1), add_edge(j, s, 0); for (int k = 0; k <= range-1; ++ k) { int barn = n+1+wanted[j][k+i]; add_edge(j, barn, 1), add_edge(barn, j, 0); } } if (isap(s,t) == n) return 1; } return 0; } int main(){ scanf("%d %d", &n, &m); node_num = n+m+2; s = 1, t = n+m+2; for (int i = 2; i <= n+1; ++ i) for (int j = 1; j <= m; ++ j) scanf("%d", &wanted[i][j]); for (int i = 1; i <= m; ++ i) scanf("%d", &cap[i]); //整数二分答案 int left = 0, right = m, ans = 40; while(left <= right) { int mid = (left + right) / 2; if (check(mid)) { right = mid - 1; ans = min(ans, mid); } else left = mid + 1; } printf("%d\n", ans); return 0; }
P2936 [USACO09JAN]Total Flow S
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; //const int maxm = 1e3 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; int m,n,s,t, x, head[maxn], num_edge; int cur[maxn],deep[maxn],last[maxn],num[maxn]; int node_num; int cap[40]; int wanted[maxn][40]; //cur当前弧优化; last该点的上一条边; num桶 用来GAP优化 struct Edge{ int next,to,dis; }edge[maxn*2]; void add_edge(int from,int to,int dis) { edge[num_edge].to=to; edge[num_edge].dis=dis; edge[num_edge].next=head[from]; head[from]=num_edge++; } //bfs仅用于更新deep void bfs(int t) { queue<int>q; for (int i=0; i<=node_num; i++) cur[i]=head[i]; for (int i=1; i<=node_num; i++) deep[i]=node_num; deep[t]=0; q.push(t); while (!q.empty()) { int now=q.front(); q.pop(); for (int i=head[now]; i != -1; i=edge[i].next) { if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边 { deep[edge[i].to] = deep[now]+1; q.push(edge[i].to); } } } } int add_flow(int s,int t) { int ans=inf,now=t; while (now!=s) { ans=min(ans,edge[last[now]].dis); now=edge[last[now]^1].to; } now=t; while (now!=s) { edge[last[now]].dis-=ans; edge[last[now]^1].dis+=ans; now=edge[last[now]^1].to; } return ans; } int isap(int s,int t) { int now=s; int maxflow = 0; bfs(t);//搜出一条增广路 for (int i=1; i<=node_num; i++) num[deep[i]]++; while (deep[s]<node_num) { if (now==t) {//如果到达汇点就直接增广,重新回到源点进行下一轮增广 maxflow+=add_flow(s,t); now=s;//回到源点 } bool has_find=0; for (int i=cur[now]; i!=-1; i=edge[i].next) { if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路 { has_find=true; cur[now]=i;//当前弧优化 now=edge[i].to; last[edge[i].to]=i; break; } } if (!has_find)//没有找到出边,重新编号 { int minn=node_num-1; for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径 if (edge[i].dis) minn=min(minn,deep[edge[i].to]); if ((--num[deep[now]])==0) break;//GAP优化 出现了断层 num[deep[now]=minn+1]++; cur[now]=head[now]; if (now!=s) now=edge[last[now]^1].to; } } return maxflow; } void init() { num_edge = 0; memset(head,-1,sizeof(head)); } int main() { init(); scanf("%d", &n); s = 1, t = 26; node_num = 100; for (int i = 1; i <= n; i++) { char tu, tv; int u, v, w; scanf("\n%c %c %d", &tu, &tv, &w); u = tu-64, v = tv-64; add_edge(u, v, w); add_edge(v, u, 0);//正反向建图 } printf("%d\n", isap(s, t)); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; struct Edge{ int to; int next; int w; }edge[maxn*2]; int head[maxn]; int cur[maxn];//当前弧优化数组 int n, m, S, T, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 int node_num; int rec[100+5][100+5]; inline void init(){ cnt = 0; memset(head, -1, sizeof head); } inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } inline void Add_Edge(int u, int v, int w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline int dfs(int u, int flow){ if(u == T) return flow; int del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 int ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline int Dinic(){ int ans = 0; while(Bfs()){ for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } bool judge(int mid) { init(); for (int i = 1; i <= n; ++ i) { Add_Edge(S, i, mid); Add_Edge(i, i+n, k); for (int j = 1; j <= n; ++j) { if (rec[i][j]) Add_Edge(i, j+2*n, 1); else Add_Edge(i+n, j+3*n, 1); } } for (int i = 1; i <= n; ++ i) { Add_Edge(i+3*n,i+2*n, k); Add_Edge(i+2*n, T, mid); } return Dinic() == mid * n; } int main() { init(); scanf("%d %d", &n, &k); S = 0, T = 4*n+1; node_num = T; for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= n; ++ j) { char temp; cin >> temp; if (temp == 'Y') rec[i][j] = 1; } } int left = 0, right = 100000; int ans = 0; while (left <= right) { int mid = (left + right) / 2; if (judge(mid)) { left = mid + 1; ans = max(ans, mid); } else right = mid - 1; } cout << ans << endl; return 0; }
分层图网络流:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; //const int maxm = 1e3 * 2 + 10; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; int m,n,S,T,k, x, head[maxn], num_edge; int cur[maxn],deep[maxn],last[maxn],num[maxn]; int node_num; int cap[40]; int wanted[maxn][40]; //cur当前弧优化; last该点的上一条边; num桶 用来GAP优化 struct Edge{ int next,to,dis; }edge[maxn*2]; void add_edge(int from,int to,int dis) { edge[num_edge].to=to; edge[num_edge].dis=dis; edge[num_edge].next=head[from]; head[from]=num_edge++; edge[num_edge].to=from; edge[num_edge].dis=0; edge[num_edge].next=head[to]; head[to]=num_edge++; } //bfs仅用于更新deep void bfs(int T) { queue<int>q; for (int i=0; i<=node_num; i++) cur[i]=head[i]; for (int i=0; i<=node_num; i++) deep[i]=node_num; deep[T]=0; q.push(T); while (!q.empty()) { int now=q.front(); q.pop(); for (int i=head[now]; i != -1; i=edge[i].next) { if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边 { deep[edge[i].to] = deep[now]+1; q.push(edge[i].to); } } } } int add_flow(int S,int T) { int ans=inf,now=T; while (now!=S) { ans=min(ans,edge[last[now]].dis); now=edge[last[now]^1].to; } now=T; while (now!=S) { edge[last[now]].dis-=ans; edge[last[now]^1].dis+=ans; now=edge[last[now]^1].to; } return ans; } int isap(int S,int T) { int now=S; int maxflow = 0; bfs(T);//搜出一条增广路 for (int i=1; i<=node_num; i++) num[deep[i]]++; while (deep[S]<node_num) { if (now==T) {//如果到达汇点就直接增广,重新回到源点进行下一轮增广 maxflow+=add_flow(S,T); now=S;//回到源点 } bool has_find=0; for (int i=cur[now]; i!=-1; i=edge[i].next) { if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路 { has_find=true; cur[now]=i;//当前弧优化 now=edge[i].to; last[edge[i].to]=i; break; } } if (!has_find)//没有找到出边,重新编号 { int minn=node_num-1; for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径 if (edge[i].dis) minn=min(minn,deep[edge[i].to]); if ((--num[deep[now]])==0) break;//GAP优化 出现了断层 num[deep[now]=minn+1]++; cur[now]=head[now]; if (now!=S) now=edge[last[now]^1].to; } } return maxflow; } void init() { num_edge = 0; memset(head,-1,sizeof(head)); } int f[1050], p[1050], r[1050], s[1050][1050]; int Find(int x){ if(f[x]==x) return x; else return f[x]=Find(f[x]); } void un(int a,int b){ int fa=Find(a),fb=Find(b); if(fa!=fb) f[fa]=fb; } int main() { init(); scanf("%d%d%d", &n, &m, &k); for (int i = 1; i <= n+1; ++ i) f[i] = i; for (int i = 0; i < m; i++) { scanf("%d%d", &p[i], &r[i]); for (int j = 0; j < r[i]; j++) { scanf("%d", &s[i][j]); if (s[i][j] == -1) s[i][j] = n+1; if (j) un(s[i][j], s[i][j-1]); } } if (Find(0) != Find(n+1)) { printf("0\n"); } else { n += 2; S = 1001, T = 1002; node_num = 1050; add_edge(S, 0, inf); add_edge(n-1, T, inf); for (int ans = 1; ; ans++) { add_edge(S, 0+ans*n, inf);//超级源点向这一时刻的地球连边 add_edge(n-1+ans*n, T, inf);//这一时刻的月球向超级汇点连边 for (int i = 1; i < n-1; i++) add_edge(i+(ans-1)*n, i+ans*n, inf);//上下层空间站不动 for (int i = 0; i < m; i++) { int u = s[i][(ans-1)%r[i]]+(ans-1)*n; int v = s[i][ans%r[i]]+ans*n; add_edge(u, v, p[i]);//通过飞船移动 } k -= isap(S,T); if (k <= 0) { printf("%d\n", ans); break; } } } return 0; }
网络流与其他奇奇怪怪的结合:
最短路径图 + 最大流:
通过dijkstra跑出各点的dist,
之后通过判断 g[i][j] + dist[i] == dist[j]来判断该点是否在最短路径图上
之后建边,跑最大流
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; #define pii pair<long long, int> typedef long long ll; const int maxn = 1e6 + 10; const int maxm = 1e5 * 2 + 10; const ll inf = 1e18; const int mod = 1e9 + 7; struct Edge{ int to; int next; ll w; } edge[maxn*2]; int head[maxn], node_num; int cur[maxn];//当前弧优化数组 int n, m, S, T, k;//点数,边数,源点,汇点 int dis[maxn];//Bfs深度 int cnt = 0;//边数 ll g[505][505]; ll diss[maxn]; bool vis[maxn]; inline void init(){ cnt = 0; for (int i = 0; i <= node_num; ++ i) head[i] = -1, diss[i] = inf, vis[i] = 0; memset(g, -1, sizeof(g)); } inline void Add_Edge(int u, int v, ll w){ edge[cnt].next = head[u]; edge[cnt].to = v; edge[cnt].w = w; head[u] = cnt++; edge[cnt].next = head[v]; edge[cnt].to = u; edge[cnt].w = 0; head[v] = cnt++; } inline bool Bfs(){ for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定 dis[S] = 0; queue<int> q; q.push(S); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量 dis[v] = dis[u] + 1; q.push(v); } } } return dis[T] != -1; } inline ll dfs(int u, ll flow){ if(u == T) return flow; ll del = flow; for(int i = cur[u]; i != -1; i = edge[i].next){ cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间 int v = edge[i].to; if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0 ll ans = dfs(v, min(del, edge[i].w));//木桶原理 edge[i].w -= ans;//正向弧减增广流量 edge[i ^ 1].w += ans;//反向弧加增广流量 del -= ans;//总流量减增广流量 if(del == 0) break;//总流量为0则不继续增广 } } return flow - del;//返回本次增广的流量 } inline ll Dinic() { ll ans = 0; while(Bfs()) { for(int i = 0; i <= node_num; ++i) cur[i] = head[i]; ans += dfs(S, inf); } return ans; } void dijkstra() { priority_queue<pii,vector<pii>,greater<pii> > q;//从小到大 diss[1] = 0; q.push({diss[1],1}); while (!q.empty()) { int now = q.top().second; q.pop(); if(vis[now]) continue; vis[now] = 1; for(int v = 1; v <= n; ++ v) { if (g[now][v] != -1) if(diss[v] > diss[now] + g[now][v]) { diss[v] = diss[now] + g[now][v]; q.push({diss[v],v}); } } } } int main() { scanf("%d %d", &n, &m); S = 0, T = n+n+1; node_num = T; init(); for (int i = 1, u, v; i <= m; ++ i) { ll w; scanf("%d %d %lld", &u, &v, &w); if (g[u][v] != -1) g[u][v] = g[v][u] = min(g[v][u], w); else g[u][v] = g[v][u] = w; } dijkstra(); ll rec[n+1]; Add_Edge(S, 1, inf), Add_Edge(n*2, T, inf); for (int i = 1; i <= n; ++ i) { scanf("%lld", &rec[i]); if (i == 1 || i == n) Add_Edge(i, i+n, inf); else Add_Edge(i, i+n, rec[i]); } for (int u = 1; u <= n; ++ u) { for (int v = 1; v <= n; ++ v) { if (u == v) continue; if (g[u][v] != -1 && diss[v] == g[u][v] + diss[u]) Add_Edge(u+n, v, inf);//, cout << u << " "<< v << endl; } } printf("%lld\n", Dinic()); return 0; }