zoukankan      html  css  js  c++  java
  • 网络流板子

    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;
    }
    View Code

    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;
    }
    View Code

    原始对偶(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;
    }
    View Code

     上面那个是直接用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;
    }
    View Code

    但是对于动态开点的题目貌似上面的代码不是很好用。所以存一个动态开点费用流

    #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; 
    }
    View Code

    存一个最小费用循环流的代码:

    看了紫书看半天都没搞懂这个东西要求什么。

    那就只能总结一下这个题目的特点,看看这个算法到底什么时候能套吧。

    给出一张有向图,从中选出权和最大的边集,组成若干个有向环。

    方法是根据环来增广(我也不懂我在讲什么

    #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;
    }
    View Code
  • 相关阅读:
    【leetcode】1020. Partition Array Into Three Parts With Equal Sum
    【leetcode】572. Subtree of Another Tree
    【leetcode】123. Best Time to Buy and Sell Stock III
    【leetcode】309. Best Time to Buy and Sell Stock with Cooldown
    【leetcode】714. Best Time to Buy and Sell Stock with Transaction Fee
    【leetcode】467. Unique Substrings in Wraparound String
    【leetcode】823. Binary Trees With Factors
    【leetcode】143. Reorder List
    【leetcode】1014. Capacity To Ship Packages Within D Days
    【leetcode】1013. Pairs of Songs With Total Durations Divisible by 60
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/13378432.html
Copyright © 2011-2022 走看看