最大流
基本问题,基于有向图。
- 流量守恒
- 反对称性
- 容量限制
算法
- “增广路”算法。Edmonds-Karp、Dinic、ISAP
- “预流推进”算法。HLPP
Ford-Fulkerson方法
数据结构课讲的那个。重点在于residual network和其反向路径。
Edmonds-Karp
最简单,复杂度高,小图,因此直接邻接矩阵。
O
(
V
E
2
)
O(VE^2)
O(VE2)
HDU 1532 Drainage Ditches Edmonds-Karp模板
#include<bits/stdc++.h>
using namespace std;
const int maxn = 202;
const int inf = 0x7f7f7f;
int graph[maxn][maxn],pre[maxn],n,m;
int bfs(int s,int t){
int flow[maxn];
memset(pre,-1,sizeof(pre));
flow[s]=inf;pre[s]=0;
queue<int>Q; Q.push(s);
while(!Q.empty()){
int u=Q.front();Q.pop();
if(u==t) break; // 找到一条路径就跑
for(int i=1;i<=m;++i){
if(i!=s&&graph[u][i]>0&&pre[i]==-1){
pre[i]=u;
Q.push(i);
flow[i]=min(flow[u],graph[u][i]);
}
}
}
if(pre[t]==-1) return -1;
return flow[t];
}
int maxflow(int s,int t){
int maxflow = 0;
while(1){
int flow = bfs(s,t);
if(flow==-1) break;
int cur = t;
while(cur!=s){ // 更新残留网络
int father = pre[cur];
graph[father][cur]-=flow;
graph[cur][father]+=flow;
cur=father;
}
maxflow += flow;
}
return maxflow;
}
// 有向有环图,求最大流。(模板题)
int main(){
while(~scanf("%d%d",&n,&m)){
memset(graph,0,sizeof(graph));
for(int i=1;i<=n;++i){
int u,v,val;scanf("%d%d%d",&u,&v,&val);
graph[u][v]+=val; // 重边的可能
}
printf("%d\n",maxflow(1,m));
}
}
Dinic
层次图、阻塞流、多路增广。
HDU 3549 Flow Problem 最大流模板题
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1002;
const int INF = 0x7f7f7f7f;
// lrj的板子
struct Dinic{
int n,m,s,t;
struct Edge{
int from,to,cap,flow;
};
vector<Edge> edges;
vector<int> G[maxn];
void AddEdge(int from,int to,int cap){
edges.push_back((Edge){from,to,cap,0});
edges.push_back((Edge){to,from,0,0});
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
};
bool vis[maxn];
int d[maxn];
int cur[maxn];
bool BFS() {
memset(vis,0,sizeof(vis));
queue<int> Q;
Q.push(s);
d[s]=0;
vis[s]=1;
while(!Q.empty()) {
int x= Q.front();Q.pop();
for(int i=0;i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow) {
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a){
if(x==t||a==0) return a;
int flow = 0,f;
for(int& i=cur[x];i<G[x].size();++i){
Edge& e = edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0) {
e.flow += f;
edges[G[x][i]^1].flow -= f;
flow += f;
a -= f;
if(a==0) break;
}
}
return flow;
}
int Maxflow(int s,int t){
this->s=s;this->t=t;
int flow=0;
while(BFS()){
memset(cur,0,sizeof(cur));
flow += DFS(s,INF);
}
return flow;
}
};
int cas=0;
void solve(){
Dinic cur;
int n,m;cin>>n>>m;
cur.n=n,cur.m=m;
for(int i=1;i<=m;++i) {
int x,y,c;scanf("%d%d%d",&x,&y,&c);
cur.AddEdge(x,y,c);
}
printf("Case %d: %d\n",++cas,cur.Maxflow(1,n));
}
int main(){
int t;cin>>t;
while(t--) solve();
}
ISAP
优化Dinic,不用重复标记深度(单词BFS)。每次用邻接点更改回退边(增广失败的边)的点的d值。+gap优化。while实现DFS,防止爆栈。
HDU 4280 Island Transport ISAP模板题
数据范围2<=N,M<=100,000。无向图。
无向图:把边改成有向边,来回方向cap都为val即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100004;
const int inf = 0x7f7f7f7f;
vector<int>G[maxn]; // 放进ISAP会爆空间。。
struct ISAP {
int n,m,s,t;
struct Edge{
int from,to,cap,flow;
};
vector<Edge> edges;
void AddEdge(int from,int to,int cap){
edges.push_back((Edge){from,to,cap,0});
edges.push_back((Edge){to,from,cap,0});
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
int p[maxn];
int num[maxn];
int d[maxn];
int cur[maxn];
int vis[maxn];
int BFS(){
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
queue<int>Q;
Q.push(t);
d[t]=0;
vis[t]=1;
while(!Q.empty()){
int x=Q.front();Q.pop();
for(int i=0;i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(!vis[e.to]){
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return d[s];
}
// while循环实现dfs
int Augment() {
int x=t,a=inf;
while(x!=s){
Edge& e = edges[p[x]];
a=min(a,e.cap-e.flow);
x=edges[p[x]].from;
}
x=t;
while(x!=s){
edges[p[x]].flow += a;
edges[p[x]^1].flow -= a;
x=edges[p[x]].from;
}
return a;
}
int Maxflow(int s,int t){
this->s=s,this->t=t;
int flow=0;
if(!BFS()) return 0;
memset(num,0,sizeof(num));
for(int i=1;i<=n;++i) num[d[i]]++;
int x=s;
memset(cur,0,sizeof(cur));
while(d[s]<n){
// for 循环实现dfs
if(x==t){
flow += Augment();
x=s;
}
int ok=0;
for(int i=cur[x];i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(e.cap>e.flow&&d[x]==d[e.to]+1) { // advance
ok=1;
p[e.to]=G[x][i];
cur[x]=i;
x=e.to;
break;
}
}
if(!ok) { // retreat
int mn=n-1;
for(int i=0;i<G[x].size();++i){
Edge& e = edges[G[x][i]];
if(e.cap>e.flow) mn=min(mn,d[e.to]);
}
if(--num[d[x]]==0) break; // gap
num[d[x]=mn+1]++;
cur[x]=0;
if(x!=s) x=edges[p[x]].from;
}
}
return flow;
}
};
void solve(){
int n,m;scanf("%d%d",&n,&m);
int minx=inf,idmin,idmax,maxx=-1;
for(int i=1;i<=n;++i){
int x,y;scanf("%d%d",&x,&y);
if(x<minx) idmin=i,minx=x;
if(x>maxx) idmax=i,maxx=x;
}
for(int i=1;i<=n;++i) G[i].clear();
ISAP cur;
cur.n=n;
cur.s=idmin,cur.t=idmax;
for(int i=1;i<=m;++i){
int x,y,z;scanf("%d%d%d",&x,&y,&z);
cur.AddEdge(x,y,z);
}
printf("%d\n",cur.Maxflow(cur.s,cur.t));
}
int main(){
int t;cin>>t;
while(t--) solve();
}
HDU 1956 Sightseeing tour 混合图的欧拉回路
先判断连通性,这道题题目保证连通。
将无向边任意指一个方向,统计所有点的度数。出现奇数度点则不存在欧拉回路。
度数定义:出度-入度。
新建两个节点,s=1和t=n+2,把中间点往后挪一位。s指向所有度数>0的点,边权为度数/2。所有度数<0的点指向t,边权为-度数/2。
跑最大流。如果s出去的所有边满流,存在欧拉回路;否则不存在。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 204;
const int inf = 0x7f7f7f7f;
vector<int>G[maxn];
struct ISAP {
int n,m,s,t;
struct Edge{
int from,to,cap,flow;
};
vector<Edge> edges;
void AddEdge(int from,int to,int cap){
edges.push_back((Edge){from,to,cap,0});
edges.push_back((Edge){to,from,0,0});
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
int p[maxn];
int num[maxn];
int d[maxn];
int vis[maxn];
int cur[maxn];
int BFS(){
memset(d,0,sizeof(d)); // 不能将其初始化为-1。因为后面有num[d[i]],会出现未知错误!(找bug找了巨久)血的教训
memset(vis,0,sizeof(vis));
queue<int>Q;
Q.push(t);
d[t]=0;
vis[t]=1;
while(!Q.empty()){
int x=Q.front();Q.pop();
for(int i=0;i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(!vis[e.to]){
d[e.to]=d[x]+1;
vis[e.to]=1;
Q.push(e.to);
}
}
}
return d[s];
}
int Augment() {
int x=t,a=inf;
while(x!=s){
Edge& e = edges[p[x]];
a=min(a,e.cap-e.flow);
x=edges[p[x]].from;
}
x=t;
while(x!=s){
edges[p[x]].flow += a;
edges[p[x]^1].flow -= a;
x=edges[p[x]].from;
}
return a;
}
int Maxflow(){
int flow=0;
if(!BFS()) return 0;
memset(num,0,sizeof(num));
for(int i=1;i<=n;++i) num[d[i]]++;
int x=s;
memset(cur,0,sizeof(cur));
while(d[s]<n){
if(x==t){
flow += Augment();
x=s;
}
int ok=0;
for(int i=cur[x];i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(e.cap>e.flow&&d[x]==d[e.to]+1) { // advance
ok=1;
p[e.to]=G[x][i];
cur[x]=i;
x=e.to;
break;
}
}
if(!ok) { // retreat
int mn=n-1;
for(int i=0;i<G[x].size();++i){
Edge& e = edges[G[x][i]];
if(e.cap>e.flow) mn=min(mn,d[e.to]);
}
if(--num[d[x]]==0) break; // gap
num[d[x]=mn+1]++;
cur[x]=0;
if(x!=s) x=edges[p[x]].from;
}
}
return flow;
}
};
int degree[maxn];
void solve(){
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n+2;++i) G[i].clear();
memset(degree,0,sizeof(degree));
ISAP cur;cur.n=n+2,cur.s=1,cur.t=n+2;
for(int i=1;i<=m;++i){
int u,v,opt;scanf("%d%d%d",&u,&v,&opt);
degree[++v]--;
degree[++u]++;
if(opt!=1) cur.AddEdge(u,v,1);
}
int sum=0;
for(int i=2;i<=n+1;++i){
if(abs(degree[i])&1) {cout<<"impossible"<<endl;return;}
if(degree[i]>0) {
cur.AddEdge(1,i,degree[i]/2);
sum+=degree[i]/2;
}
if(degree[i]<0) cur.AddEdge(i,n+2,-degree[i]/2);
}
if(sum==cur.Maxflow()) cout<<"possible"<<endl;
else cout<<"impossible"<<endl;
}
int main(){
int t;cin>>t;
while(t--) solve();
}
HDU 3472 HS BDC 混合图欧拉路
(1)判断连通性
(2)度数:两个点的度数是奇数:欧拉路
(3)将无向边连接。并多连一条两个奇数点之间的边。
(4)用欧拉回路的办法判断。
PS. 没看清输入是opt=1时是无向边,导致debug3小时。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 30;
const int inf = 0x7f7f7f7f;
vector<int>G[maxn];
struct DSU {
int fa[maxn],vis[maxn];
void init(int n){
for(int i=1;i<=n;++i) fa[i]=i;
memset(vis,0,sizeof(vis));
}
int find(int x){
return fa[x]=(fa[x]==x?x:find(fa[x]));
}
void unite(int x,int y){
vis[x]=vis[y]=1;
x=find(x),y=find(y);
if(x==y) return;
fa[x]=y;
}
int num(int n){
int cnt=0;
for(int i=1;i<=n;++i)
if(vis[i]&&fa[i]==i) cnt++;
return cnt;
}
};
struct ISAP {
int n,m,s,t;
struct Edge{
int from,to,cap,flow;
};
vector<Edge> edges;
void Addedge(int from,int to,int cap){
edges.push_back((Edge){from,to,cap,0});
edges.push_back((Edge){to,from,0,0});
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
int p[maxn];
int num[maxn];
int mp[maxn];
int d[maxn];
int cur[maxn];
int vis[maxn];
int BFS(){
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
queue<int> Q;
Q.push(t);
d[t]=0;
vis[t]=1;
n=1;
mp[n]=t;
while(!Q.empty()){
int x=Q.front();Q.pop();
for(int i=0;i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(!vis[e.to]){
++n;
vis[e.to]=1;
mp[n]=e.to;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return d[s];
}
int Augment(){
int x=t,a=inf;
while(x!=s){
Edge& e=edges[p[x]];
a=min(a,e.cap-e.flow);
x=edges[p[x]].from;
}
x=t;
while(x!=s){
edges[p[x]].flow+=a;
edges[p[x]^1].flow-=a;
x=edges[p[x]].from;
}
return a;
}
int Maxflow(int s,int t){
this->s=s,this->t=t;
int flow=0;
if(!BFS()) return 0;
memset(num,0,sizeof(num));
for(int i=1;i<=n;++i) num[d[mp[i]]]++;
int x=s;
memset(cur,0,sizeof(cur));
while(d[s]<n){
if(x==t) flow+=Augment(),x=s;
int ok=0;
for(int i=cur[x];i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(e.cap>e.flow&&d[x]==d[e.to]+1) {
ok=1;
p[e.to]=G[x][i];
cur[x]=i;
x=e.to;
break;
}
}
if(!ok){
int mn=n-1;
for(int i=0;i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(e.cap>e.flow) mn=min(mn,d[e.to]);
}
if(--num[d[x]]==0) break;
num[d[x]=mn+1]++;
cur[x]=0;
if(x!=s) x=edges[p[x]].from;
}
}
return flow;
}
};
int degree[maxn];
int cas;
void solve(){
int n;cin>>n;
ISAP cur;DSU cur2;
cur2.init(28);
memset(degree,0,sizeof(degree));
for(int i=1;i<=28;++i) G[i].clear();
for(int i=1;i<=n;++i){
int opt;string s;cin>>s;
int u=s[0]-'a'+1,v=s[s.length()-1]-'a'+1;
scanf("%d",&opt);
degree[++u]++;
degree[++v]--;
cur2.unite(u,v);
if(opt) cur.Addedge(u,v,1);
}
cout<<"Case "<<++cas<<": ";
if(cur2.num(28)!=1) {cout<<"Poor boy!"<<endl;return;}
int sum=0,s=inf,t=inf,count=0;
for(int i=2;i<=27;++i){
if(abs(degree[i])&1){
if(count==0) s=i;
else t=i;
count++;
}
if(degree[i]>0) {
cur.Addedge(1,i,degree[i]/2);
sum+=degree[i]/2;
}
if(degree[i]<0)
cur.Addedge(i,28,-degree[i]/2);
}
if(count!=2&&count!=0) {cout<<"Poor boy!"<<endl;return;}
if(s!=inf&&t!=inf) {
if (degree[s] < degree[t]) swap(s, t);
cur.Addedge(t,s,1);
}
if(sum==cur.Maxflow(1,28)) cout<<"Well done!"<<endl;
else cout<<"Poor boy!"<<endl;
}
int main(){
int t;cin>>t;
while(t--) solve();
}
【飞马杯】体育课排队
题意:坐标系,n个人,n个位置,曼哈顿距离。人移动速度为1.问最短需要多久,所有人到这些位置上。
思路:二分图匹配。然而问的是最小的最大权。普通二分图算法无法处理。考虑二分答案。每次加入权小于等于答案的边。然后问对该图是否构成匹配。二分图的算法会TLE。于是选择网络流ISAP算法。
#include<bits/stdc++.h>
using namespace std;
#define maxn 2005
#define maxm 2004005
#define INF 1234567890
int T, n, m, ss[maxn], sx[maxn], sy[maxn], sum[maxn], l, r, s, t, maxflow, cnt = 1,
x[maxn], y[maxn], head[maxn], dis[maxn], cur[maxn], gap[maxn], ans[maxn];
struct Edge {
int u, v, w, pre, next;
} edge[maxm];
inline void add(int u, int v, int w) {
edge[++cnt].u = u;
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].pre = w;
edge[cnt].next = head[u];
head[u] = cnt;
return;
}
inline int Dfs(int u, int lim) {
if (!lim || u == t) return lim;
int flow = 0, f;
for (int i = cur[u]; i; i = edge[i].next) {
int v = edge[i].v, w = edge[i].w;
cur[u] = i;
if (dis[v] + 1 == dis[u] && (f = Dfs(v, min(lim, w)))) {
flow += f;
lim -= f;
edge[i].w -= f;
edge[i ^ 1].w += f;
if (!lim) return flow;
}
}
gap[dis[u]]--;
if (!gap[dis[u]]) dis[s] = t + 1;
dis[u]++;
gap[dis[u]]++;
return flow;
}
inline void Bfs() {
queue<int> q;
for (int i = 1; i <= t; i++) {
dis[i] = INF;
gap[i] = 0;
}
dis[t] = 0;
gap[0] = 1;
q.push(t);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if (dis[v] >= INF) {
dis[v] = dis[u] + 1;
gap[dis[v]]++;
q.push(v);
}
}
}
return;
}
inline void ISAP() {
Bfs();
while (dis[s] < t) {
for (int i = 1; i <= t; i++)
cur[i] = head[i];
maxflow += Dfs(s, INF);
}
}
inline bool Check(int mid) {
cnt = 1;
maxflow = 0;
for (int i = 1; i <= t; i++)
head[i] = dis[i] = cur[i] = gap[i] = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
for (int k = 1; k <= ss[j]; k++) {
int w = abs(sx[j] + k - 1 - x[i]) + abs(sy[j] - y[i]);
if (w > mid) continue;
add(i, n + sum[j - 1] + k, 1);
add(n + sum[j - 1] + k, i, 0);
}
for (int i = 1; i <= n; i++) {
add(s, i, 1);
add(i, s, 0);
add(i + n, t, 1);
add(t, i + n, 0);
}
ISAP();
if (maxflow == n) return 1;
else return 0;
}
int main() {
cin >> T;
while (T--) {
cin >> n >> m;
l = 0, r = 5000;
for (int i = 1; i <= n; i++)
cin >> x[i] >> y[i];
for (int i = 1; i <= m; i++) {
cin >> ss[i] >> sx[i] >> sy[i];
sum[i] = sum[i - 1] + ss[i];
}
s = 2 * n + 1;
t = 2 * n + 2;
while (l < r) {
int mid = (l + r) >> 1;
if (Check(mid)) r = mid;
else l = mid + 1;
}
cout << l << endl;
Check(l);
for (int i = 1; i <= n; i++)
for (int j = head[i]; j; j = edge[j].next) {
int v = edge[j].v, w = edge[j].w, pre = edge[j].pre;
if (pre > w) ans[v - n] = i;
}
for (int i = 1; i <= m; i++)
for (int j = 1; j <= ss[i]; j++)
cout << ans[sum[i - 1] + j] << " \n"[j == ss[i]];
}
return 0;
}
最小割
HDU 3251 Being a Hero 最小割
题意:给n个城市,m条有向边。每条边有权值,如今有些城市能够选择得到。可选的城市有一个价值。可是要满足从1到达不了这些城市,为了满足要求能够去掉一些边,须要花费边的权值,问终于得到的最大价值是多少,并给出方案。(抄别人的题意)
思路:
(1)可选的城市连一条边去到一个新节点
t
t
t,权值为该城市的价值。
(2)那么最大价值=可选城市总价值-最大流。
(3)why?最大流后,图被分成两个部分,一个是包含
s
s
s 的部分,一个是包含
t
t
t 的部分。连通这两个部分的边的权值和为最大流,这些边就是最小割的割边。这些边中若存在可选城市指向
t
t
t 的边,说明这个可选城市不需要被选,否则,说明需要被选。才能成就最大流/最小割。也可以这样想:假设全部可选城市都选上。计算最小割,若某个城市连向终点的边被割掉了,那说明不要这个城市的价值,才能成就最小割。所以是总价值减去最大流。
(4)如何给出割边?在残留网络跑一次BFS,得到包含
s
s
s 的部分,剩下的点包含在另一部分,包含
t
t
t 的部分。连接这两个部分的边就是最小割。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000;
const int inf = 0x7f7f7f7f;
vector<int> G[maxn];
int is[maxn];
struct ISAP {
int n,m,s,t;
struct Edge{
int from,to,cap,flow;
};
vector<Edge> edges;
void Addedge(int from,int to,int cap){
edges.push_back((Edge){from,to,cap,0});
edges.push_back((Edge){to,from,0,0});
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
int p[maxn];
int num[maxn];
int d[maxn];
int cur[maxn];
int vis[maxn];
int BFS(){
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
queue<int> Q;
Q.push(t);
d[t]=0;
vis[t]=1;
while(!Q.empty()){
int x=Q.front();Q.pop();
for(int i=0;i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(!vis[e.to]){
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return d[s];
}
int Augment(){
int x=t,a=inf;
while(x!=s){
Edge& e=edges[p[x]];
a=min(a,e.cap-e.flow);
x=edges[p[x]].from;
}
x=t;
while(x!=s){
edges[p[x]].flow+=a;
edges[p[x]^1].flow-=a;
x=edges[p[x]].from;
}
return a;
}
int Maxflow(int s,int t){
this->s=s,this->t=t;
int flow=0;
if(!BFS()) return 0;
memset(num,0,sizeof(num));
for(int i=1;i<=n;++i) num[d[i]]++;
int x=s;
memset(cur,0,sizeof(cur));
while(d[s]<n){
if(x==t) flow+=Augment(),x=s;
int ok=0;
for(int i=cur[x];i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(e.cap>e.flow&&d[x]==d[e.to]+1) {
ok=1;
p[e.to]=G[x][i];
cur[x]=i;
x=e.to;
break;
}
}
if(!ok){
int mn=n-1;
for(int i=0;i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(e.cap>e.flow) mn=min(mn,d[e.to]);
}
if(--num[d[x]]==0) break;
num[d[x]=mn+1]++;
cur[x]=0;
if(x!=s) x=edges[p[x]].from;
}
}
return flow;
}
void getedge(){
memset(vis,0,sizeof(vis));
vis[1]=1;
queue<int> Q;
Q.push(1);
while(!Q.empty()) {
int x = Q.front();
Q.pop();
for (int i = 0; i < G[x].size(); ++i) {
Edge &e = edges[G[x][i]];
if (!vis[e.to] && e.cap > e.flow) {
vis[e.to] = 1;
Q.push(e.to);
}
}
}
vector<int> ed;
for(int i=0;i<m;i+=2){
if(edges[i].to!=t&&vis[edges[i].from]&&!vis[edges[i].to])
ed.push_back(i/2+1);
}
cout<<ed.size()<<" ";
for(int i=0;i<ed.size()-1;++i) cout<<ed[i]<<" ";
cout<<ed[ed.size()-1]<<endl;
}
};
int cas=0;
void solve(){
int n,m,f;cin>>n>>m>>f;
ISAP cur;cur.n=n+1;
for(int i=1;i<=n+1;++i) G[i].clear();
memset(is,0,sizeof(is));
for(int i=1;i<=m;++i){
int u,v,c;scanf("%d%d%d",&u,&v,&c);
cur.Addedge(u,v,c);
}
int sum=0;
for(int i=1;i<=f;++i){
int u,c;scanf("%d%d",&u,&c);
is[u]=1;
sum+=c;
cur.Addedge(u,n+1,c);
}
int mincut=cur.Maxflow(1,n+1);
cout<<"Case "<<++cas<<": "<<sum-mincut<<endl;
cur.getedge();
}
int main(){
int t;cin>>t;
while(t--) solve();
}
POJ 1815 Friendship
题意:无向图。删最少的点使得S和T不连通。求删的点数,最小字典序输出点集。
思路:经典题。如果是删边那么直接最小割,原图原边边权为1就可以做出来。但是此处是删点,考虑拆点:把一个点拆成两个点:入和出。连一条边,从入点连到出点,边权为1。我们不希望原图的边被拆,则将那些边的边权设为inf。这样,跑最大流(最小割)出来,被删的就是连接原图的一个点出和入的边。也就是说,被删的就是点。然后如何输出最小字典序点集,考虑暴力,对每个点,尝试去掉他再重新建图跑最小割,如果最小割的值变小1,则当前点就被删了。注意:去掉这个点仅仅是去掉这个点内部的那条边,与之相关的边都会跑不了。然后再去掉第二个点,看最小割是否又小了1,以此类推。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#define in(x) x
#define out(x) (x+n)
using namespace std;
const int maxn = 403;
const int inf = 0x7f7f7f7f;
vector<int> G[maxn];
int A[maxn][maxn];
int isdel[maxn];
struct ISAP {
int n,m,s,t;
struct Edge{
int from,to,cap,flow;
};
vector<Edge> edges;
void AddEdge(int from,int to,int cap){
edges.push_back((Edge){from,to,cap,0});
edges.push_back((Edge){to,from,0,0});
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
void build(){
edges.clear();
for(int i=0;i<=2*n;++i) G[i].clear();
for(int i=1;i<=n;++i)
if(!isdel[i]) AddEdge(in(i),out(i),1);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(A[i][j]) AddEdge(out(i),in(j),inf);
}
int p[maxn];
int num[maxn];
int d[maxn];
int vis[maxn];
int cur[maxn];
int BFS(){
memset(d,0,sizeof(d));
memset(vis,0,sizeof(vis));
queue<int>Q;
Q.push(t);
d[t]=0;
vis[t]=1;
while(!Q.empty()){
int x=Q.front();Q.pop();
for(int i=0;i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(!vis[e.to]){
d[e.to]=d[x]+1;
vis[e.to]=1;
Q.push(e.to);
}
}
}
return d[s];
}
int Augment() {
int x=t,a=inf;
while(x!=s){
Edge& e = edges[p[x]];
a=min(a,e.cap-e.flow);
x=edges[p[x]].from;
}
x=t;
while(x!=s){
edges[p[x]].flow += a;
edges[p[x]^1].flow -= a;
x=edges[p[x]].from;
}
return a;
}
int Maxflow(int s,int t){
this->s=s,this->t=t;
int flow=0;
if(!BFS()) return 0;
memset(num,0,sizeof(num));
for(int i=1;i<=2*n;++i) num[d[i]]++;
int x=s;
memset(cur,0,sizeof(cur));
while(d[s]<2*n){
if(x==t){
flow += Augment();
x=s;
}
int ok=0;
for(int i=cur[x];i<G[x].size();++i){
Edge& e=edges[G[x][i]];
if(e.cap>e.flow&&d[x]==d[e.to]+1) { // advance
ok=1;
p[e.to]=G[x][i];
cur[x]=i;
x=e.to;
break;
}
}
if(!ok) { // retreat
int mn=2*n-1;
for(int i=0;i<G[x].size();++i){
Edge& e = edges[G[x][i]];
if(e.cap>e.flow) mn=min(mn,d[e.to]);
}
if(--num[d[x]]==0) break; // gap
num[d[x]=mn+1]++;
cur[x]=0;
if(x!=s) x=edges[p[x]].from;
}
}
return flow;
}
};
int main(){
int n,s,t;cin>>n>>s>>t;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
scanf("%d",&A[i][j]);
if(A[s][t]) {cout<<"NO ANSWER!"<<endl;return 0;}
ISAP cur;
cur.n=n;
cur.build();
int ans=cur.Maxflow(out(s),in(t));
cout<<ans<<endl;
vector<int> res;
for(int i=1;i<=n;++i){
if(i==s) continue;
if(i==t) continue;
isdel[i]=1;
cur.build();
int newa = cur.Maxflow(out(s),in(t));
if(newa==ans-1) res.push_back(i),ans--;
else isdel[i]=0;
}
int sz=res.size();
for(int i=0;i<sz-1;++i) cout<<res[i]<<" ";
if(sz!=0) cout<<res[sz-1]<<endl;
}
最小费用最大流
POJ 2135 Farm Tour 费用流裸题
无向图:不可以像最大流那样一条边变两条正边。因为此题是无向图,所以建边的时候如果建两条费用都是正的边的话,退流时无法修正费用。
所以应该建4条边。
#include<iostream>
#include<vector>
#include<queue>
#include<string.h>
#include<cstdio>
using namespace std;
const int maxn = 1e3+4;
const int inf = 0x7f7f7f7f;
struct Edge {
int from,to,cap,flow,cost;
Edge(int u,int v,int c,int f,int w):from(u),to(v),cap(c),flow(f),cost(w){}
};
vector<int> G[maxn];
struct MCMF {
int n,m;
vector<Edge> edges;
int inq[maxn];
int d[maxn];
int p[maxn];
int a[maxn];
void init(int n){
this->n = n;
for(int i=1;i<=n;++i) G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap,int cost){
edges.push_back(Edge{from,to,cap,0,cost});
edges.push_back(Edge{to,from,0,0,-cost});
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BellmanFord(int s,int t,int& flow,long long& cost) {
for(int i=1;i<=n;++i) d[i]=inf;
memset(inq,0,sizeof(inq));
d[s] = 0;inq[s] = 1;p[s] = 0;a[s] = inf;
queue<int> Q;
Q.push(s);
while(!Q.empty()){
int u=Q.front();Q.pop();
inq[u]=0;
for(int i=0;i<G[u].size();++i){
Edge& e = edges[G[u][i]];
if(e.cap>e.flow&&d[e.to]>d[u]+e.cost){
d[e.to]=d[u]+e.cost;
p[e.to]=G[u][i];
a[e.to]=min(a[u],e.cap-e.flow);
if(!inq[e.to]) {Q.push(e.to);inq[e.to]=1;}
}
}
}
if(d[t]==inf) return false;
flow += a[t];
cost += (long long)d[t] * (long long)a[t];
for(int u=t;u!=s;u=edges[p[u]].from) {
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];
}
return true;
}
int MincostMaxflow(int s,int t,long long& cost){
int flow = 0;cost = 0;
BellmanFord(s,t,flow,cost);
BellmanFord(s,t,flow,cost);
return flow;
}
};
int main(){
int n,m;
scanf("%d%d",&n,&m);
MCMF cur;
cur.init(n);
for (int i = 1; i <= m; ++i) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
cur.AddEdge(u, v, 1, c);
cur.AddEdge(v, u, 1, c);
}
long long cost;
cur.MincostMaxflow(1, n, cost);
cout << cost << endl;
}
拆点+费用流。求最大:把费用变成负数即可。两条路:跑两次bellmanford即可。这题卡内存。。邻接表过不了,Edges结构体不能写成有Edge(int …)😦){}的样子。不然就MLE()
就当是写了个链式前向星的板子吧()。
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x7f7f7f7f;
#define in(x) x
#define out(x) (x+n*n)
// 拆点 + 最大流,卡了邻接表
const int maxn = 720000;
bitset<maxn> inq;
int d[maxn];
int p[maxn];
int a[maxn];
int head[maxn];
struct Edge {
int from,to,cap,flow,cost,next;
}edges[maxn<<2];
int tot;
struct MCMF {
int n,m;
void init(int n){
this->n = n;
tot=0;
memset(head,-1,sizeof(head));
}
void AddEdge(int from,int to,int cap,int cost){
edges[tot++]=(Edge{from,to,cap,0,cost,head[from]});
edges[tot++]=(Edge{to,from,0,0,-cost,head[to]});
head[from]=tot-2;
head[to]=tot-1;
}
bool BellmanFord(int s,int t,int& flow,long long& cost) {
for(int i=0;i<n;++i) d[i]=inf;
inq.reset();
d[s] = 0;inq.set(s);p[s] = 0;a[s] = inf;
queue<int> Q;
Q.push(s);
while(!Q.empty()){
int u=Q.front();Q.pop();
inq.reset(u);
for(int i=head[u];~i;i=edges[i].next){
Edge& e = edges[i];
if(e.cap>e.flow&&d[e.to]>d[u]+e.cost){
d[e.to]=d[u]+e.cost;
p[e.to]=i; // 来的边的序号
a[e.to]=min(a[u],e.cap-e.flow);
if(!inq[e.to]) {Q.push(e.to);inq.set(e.to);}
}
}
}
if(d[t]==inf) return false;
flow += a[t];
cost += (long long)d[t] * (long long)a[t];
for(int u=t;u!=s;u=edges[p[u]].from) {
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];
}
return true;
}
int MincostMaxflow(int s,int t,long long& cost){
int flow = 0;cost = 0;
BellmanFord(s,t,flow,cost);
BellmanFord(s,t,flow,cost);
return flow;
}
};
int main(){
int n;
MCMF cur;
while(~scanf("%d",&n)){
cur.init(2*n*n);
int st,ed;
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
int tmp;
scanf("%d",&tmp);
if(i==0&&j==0) st=tmp;
if(i==n-1&&j==n-1) ed=tmp;
// cout<<out(n*(i-1)+j)<<" "<<in(n*i+j)<<endl;
if(i!=0) cur.AddEdge(out(n*(i-1)+j),in(n*i+j),inf,0);
if(j!=0) cur.AddEdge(out(n*i+j-1),in(n*i+j),1,0);
cur.AddEdge(in(n*i+j),out(n*i+j),1,-tmp);
}
}
long long cost;
int s=out(0),t=in(n*n-1);
cur.MincostMaxflow(s,t,cost);
cout<<-cost+st+ed<<endl;
}
}