Dinic模板 上界O(N*M2) 二分图用
#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); s = 1, t = 26; node_num = 100; for (int i = 1; i <= n; i++) { char tu, tv; int u, v, w; scanf(" %c %c %d", &tu, &tv, &w); u = tu-64, v = tv-64; Add_Edge(u, v, w); Add_Edge(v, u, 0);//正反向建图 } printf("%d ", Dinic()); return 0; }
ISAP模板 上界O(N2*M) 非二分图用
#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(" %c %c %d", &tu, &tv, &w); u = tu-64, v = tv-64; add_edge(u, v, w); add_edge(v, u, 0);//正反向建图 } printf("%d ", isap(s, t)); return 0; }
原始对偶(Primal-Dual)费用流算法 可处理有负边无负环
#include <bits/stdc++.h> using namespace std; typedef pair<int, int> PII; const int N = 5005, M = 50005; const int INF = 0x3f3f3f3f; int n, m, S, T; int tot = -1, head[N], cur[N]; int h[N], dis[N], vis[N]; struct Edge { int p, nxt, c, w; Edge(int p = 0, int nxt = 0, int c = 0, int w = 0) : p(p), nxt(nxt), c(c), w(w) {} } edge[M * 2]; inline void Add_Edge(int u, int v, int c, int w) { edge[++tot] = Edge(v, head[u], c, w); head[u] = tot; edge[++tot] = Edge(u, head[v], 0, -w); head[v] = tot; } bool Dijkstra() { for (int i = 1; i <= T; ++ i) dis[i] = INF; priority_queue<PII> pq; pq.push({dis[S] = 0, S}); while (!pq.empty()) { PII cur = pq.top(); pq.pop(); int u = cur.second; if (-cur.first > dis[u]) continue; for (int i = head[u]; ~i; i = edge[i].nxt) { int v = edge[i].p, c = edge[i].c, w = edge[i].w + h[u] - h[v]; if (c && dis[v] > dis[u] + w) pq.push({-(dis[v] = dis[u] + w), v}); } } return dis[T] < INF; } int DFS(int u, int c) { if (u == T) return c; int r = c; vis[u] = 1; for (int &i = cur[u]; ~i && r; i = edge[i].nxt) { int v = edge[i].p, c = edge[i].c, w = edge[i].w + h[u] - h[v]; if (!vis[v] && c && dis[u] + w == dis[v]) { int x = DFS(v, min(r, c)); r -= x; edge[i].c -= x; edge[i ^ 1].c += x; } } vis[u] = 0; return c - r; } /* 6 11 1 2 23 1 3 12 1 4 99 2 5 17 2 6 73 3 5 3 3 6 21 4 6 8 5 2 33 5 4 5 6 5 20 */ int main() { scanf("%d%d", &n, &m); S = 1, T = n*2; memset(head, -1, sizeof(head)); Add_Edge(n, T, 2, 0); Add_Edge(S, n+1, 2, 0); for (int i = 2; i <= n-1; ++ i) Add_Edge(i, i+n, 1, 0); for (int i = 1; i <= m; ++i) { int u, v, w; scanf("%d%d%d", &u, &v, &w); Add_Edge(u+n, v, 1, w); } int mf = 0, mc = 0; while (Dijkstra()) { memcpy(cur, head, sizeof(cur)); int c = DFS(S, INF); for (int i = 1; i <= T; ++i) if (dis[i] < INF) h[i] += dis[i]; mf += c; mc += c * h[T]; } printf("%d %d ", mf, mc); return 0; }
上面那个是直接用dij跑的
正规的应该是用Bellman-ford先预处理之后再dij,附上代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<ll, int> pii; struct Edge { int u, v; ll flow, cap, cost; int next; }; const int MAXN = 5000, MAXM = 50000; const ll LLINF = 0x3f3f3f3f3f3f3f3fLL; int e_ptr = 1, S, T, n, m, head[MAXN+10]; Edge E[(MAXM+10)<<1]; ll dist[MAXN+10], MaxFlow, MinCost, delta; int inq[MAXN+10], done[MAXN+10], vis[MAXN+10]; void AddEdge(int u, int v, ll cap, ll cost) { E[++e_ptr] = (Edge) { u, v, 0, cap, cost, head[u] }; head[u] = e_ptr; E[++e_ptr] = (Edge) { v, u, 0, 0, -cost, head[v] }; head[v] = e_ptr; } void Reduce() { for(int i = 2; i <= e_ptr; i++) E[i].cost += dist[E[i].v] - dist[E[i].u]; delta += dist[S]; } bool BellmanFord() { queue<int> Q; memset(dist, 0x3f, sizeof(dist)); dist[T] = 0; Q.push(T); inq[T] = true; while(!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = false; for(int j=head[u]; j; j=E[j].next) { int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost; if(f < c && dist[v] > dist[u] + len) { dist[v] = dist[u] + len; if(!inq[v]) { inq[v] = true; Q.push(v); } } } } return dist[S] != LLINF; } bool Dijkstra() { memset(dist, 0x3f, sizeof(dist)); memset(vis, 0, sizeof(vis)); priority_queue<pii,vector<pii>,greater<pii> > pq; dist[T] = 0; pq.push({dist[T], T}); while(!pq.empty()) { int u = pq.top().second; pq.pop(); if(vis[u]) continue; vis[u] = 1; for(int j=head[u]; j; j=E[j].next) { int v = E[j].v; ll f = E[j^1].flow, c = E[j^1].cap, len = E[j^1].cost; if(f < c && dist[v] > dist[u] + len) { dist[v] = dist[u] + len; pq.push({dist[v],v}); } } } return dist[S] != LLINF; } ll DFS(int u, ll flow) { if(u == T || flow == 0) return flow; vis[u] = true; // differ from dinic ll res = flow; for(int j=head[u]; j; j=E[j].next) { int v = E[j].v; ll f = E[j].flow, c = E[j].cap, len = E[j].cost; if(!vis[v] && f < c && len == 0) { // not `dist[v] == dist[u]` ! they do not equal ! ll tmp = DFS(v, min(res, c-f)); // len = 0 <=> on the shortest path E[j].flow += tmp; E[j^1].flow -= tmp; res -= tmp; } } return flow - res; } void Augment() { ll CurFlow = 0; while(memset(vis, 0, sizeof(vis)), (CurFlow = DFS(S, LLINF))) { MaxFlow += CurFlow; MinCost += CurFlow * delta; } } void PrimalDual() { if(!BellmanFord()) return; Reduce(); Augment(); while(Dijkstra()) { Reduce(); Augment(); } } int main() { int u, v, cap, cost; scanf("%d%d%d%d", &n, &m, &S, &T); for(int i=1; i<=m; i++) { scanf("%d%d%d%d", &u, &v, &cap, &cost); AddEdge(u, v, cap, cost); } PrimalDual(); printf("%lld %lld", MaxFlow, MinCost); return 0; }
但是对于动态开点的题目貌似上面的代码不是很好用。所以存一个动态开点费用流
#include <iostream> #include <iomanip> #include <cstring> #include <map> #include <queue> #define inf 2147483646 #define N 10000 using namespace std; struct ed{ int u,w,next,f; }e[1000000]; int g[1000][2000],a[2000]; int n,m,st=1,ans,cost,sm,fir[30000],c[30000],d[30000]; int vis[30000],sp[30000]; queue<int> q; bool v[30000]; map<int,int> ha; void add(int x,int y,int w,int f) { e[++st].u=y; e[st].next=fir[x]; e[fir[x]=st].w=w; e[st].f=f; e[++st].u=x; e[st].next=fir[y]; e[fir[y]=st].w=0; e[st].f=-f; } bool spfa() { for (int i=0;i<=N;i++) d[i]=inf/2,c[i]=fir[i],v[i]=0; q.push(0); v[0]=1; d[0]=0; while (!q.empty()) { int k=q.front(); q.pop(); v[k]=0; for (int i=fir[k];i;i=e[i].next){ int u=e[i].u,w=e[i].f; if (d[u]>d[k]+w&&e[i].w){ d[u]=d[k]+w; if (!v[u]) v[u]=1,q.push(u); } } } return (d[N]<inf/2); } int dfs(int p,int now) { if (p==N){v[N]=1; return now;} int mw=0; v[p]=1; for (int i=fir[p];i;i=e[i].next) { int u=e[i].u,w=e[i].f; if (d[u]==d[p]+w&&e[i].w&&(!v[u]||u==N)) if (mw=dfs(u,min(e[i].w,now))) { e[i].w-=mw; e[i^1].w+=mw; cost+=mw*w; return mw; } } } void dinic() { while (spfa()) { ans+=dfs(0,inf); for (int i=fir[N];i;i=e[i].next){ int u=e[i].u,w=e[i].w; if (w&&!vis[u]) { vis[u]=1; int co=ha[u]; sp[co]++; add(++sm,N,1,0); ha[sm]=co; for (int i=1;i<=n;i++) add(i,sm,1,sp[co]*g[i][co]); } } } } int main() { cin>>n>>m; int sum=0; for (int i=1;i<=n;i++) cin>>a[i],sum+=a[i]; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) cin>>g[i][j]; for (int i=1;i<=n;i++) add(0,i,a[i],0); sm=n; //for (int k=1;k<=n;k++) 时间K(总数不为n了) for (int j=1;j<=m;j++) {//厨师j add(++sm,N,1,0); ha[sm]=j; sp[j]=1; for (int i=1;i<=n;i++) add(i,sm,1,g[i][j]); //菜i } dinic(); cout<<cost<<endl; }
存一个最小费用循环流的代码:
看了紫书看半天都没搞懂这个东西要求什么。
那就只能总结一下这个题目的特点,看看这个算法到底什么时候能套吧。
给出一张有向图,从中选出权和最大的边集,组成若干个有向环。
方法是根据环来增广(我也不懂我在讲什么
#include <iostream> #include <cstring> #include <cstdio> #include <queue> #include <cmath> using namespace std; const int N=110; const int M=20010; const int INF=1061109567; const double dINF=1e9; const double eps=1e-6; int w[N],cnt,fir[N],to[M],nxt[M]; int cap[M],path[N],vis[N]; double val[M],dis[N]; queue<int>q; struct Net_Flow{ void Init(){memset(fir,0,sizeof(fir));cnt=1;} void add(int a,int b,int c,double v){ nxt[++cnt]=fir[a];cap[cnt]=c; to[cnt]=b;val[cnt]=v;fir[a]=cnt; } void addedge(int a,int b,int c,double v){ add(a,b,c,v);add(b,a,0,-v); } double Spfa(int S,int T){ for (int i = 0; i <= T+1; ++ i) dis[i] = dINF; q.push(S);vis[S]=1;dis[S]=0; while(!q.empty()){ int x=q.front();q.pop();vis[x]=0; for(int i=fir[x];i;i=nxt[i]) if(cap[i]&&dis[to[i]]-dis[x]-val[i]>eps){ dis[to[i]]=dis[x]+val[i]; if(!vis[to[i]])q.push(to[i]); vis[to[i]]=1;path[to[i]]=i; } } return dis[T]; } int Aug(int S,int T){ int p=T,f=INF; while(p!=S){ f=min(f,cap[path[p]]); p=to[path[p]^1]; }p=T; while(p!=S){ cap[path[p]]-=f; cap[path[p]^1]+=f; p=to[path[p]^1]; } return f; } double MCMF(int S,int T){ double v=0,d; while((d=Spfa(S,T))!=dINF) v+=d*Aug(S,T); return v; } }mcmf; int deg[N]; int a[N],b[N],G[N][N]; int sqr(int x){ return x*x; } int main(){ int n,x,y,cas=0;double ans; while(scanf("%d%d%d",&n,&x,&y)!=EOF){ if(!n)break; mcmf.Init();ans=0.0; memset(G,0,sizeof(G)); memset(deg,0,sizeof(deg)); for(int i=1;i<=n;i++){ int t; scanf("%d%d%d",&a[i],&b[i],&t); while(t){G[i][t]=1;scanf("%d",&t);} } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)if(G[i][j]){ double v=y-x*sqrt(sqr(a[i]-a[j])+sqr(b[i]-b[j])); if(v<0){ mcmf.addedge(j,i,1,-v); ans+=v;deg[j]+=1;deg[i]-=1; } if(v>0)mcmf.addedge(i,j,1,v); } for(int i=1;i<=n;i++){ if(deg[i]>0)mcmf.addedge(0,i,deg[i],0); if(deg[i]<0)mcmf.addedge(i,n+1,-deg[i],0); } printf("Case %d: %.2f ",++cas,eps-ans-mcmf.MCMF(0,n+1)); } return 0; }