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
  • 相关阅读:
    你做的页面有哪些浏览器测试过?这些浏览器的内核分别是什么?
    响应式布局
    Promise(二)
    Promise(一)
    同步和异步的自我理解
    null 与 undefined 区别
    python pandas 数据处理
    【python】list、tuple、dict、set、dataframe、narray、series之间的区别
    时间序列常用模型
    Linux系统学习笔记
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/13378432.html
Copyright © 2011-2022 走看看