zoukankan      html  css  js  c++  java
  • D2 HL 图的高级应用 网络流专题

    被网络流支配的一天;

    具体概念以后再讲,先总结题目;

    蜥蜴

    将石柱拆点,分为底端和顶端;

    每个石柱底端顶端连边,容量为高度;

    首先,超级源点向每个蜥蜴所在石柱的底部连一条为1的边;

    然后,找到能跳出去的石柱,顶端向超级汇点连一条为inf的边;

    对于地图内任意两个石柱,如果间距小于d,就将其中一根石柱的顶部与另一根石柱的底部相连,其连线容量为inf。

    总数-最大流即可;

    #include<bits/stdc++.h>
    using namespace std;
    #define N 10005
    #define inf 1e9
    template<typename T>inline void read(T &x)
    {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    
    int n,m,d,s,t,tot=1,num,k;
    int lin[N],dep[N],X[N],Y[N],zn[1000][1000],hi[201][201];
    char s2[201][201],s1[201][201];
    struct gg {
        int flow,y,next;
    }a[500000];
    
    inline void add(int x,int y,int flow) {
        a[++tot].y=y; a[tot].next=lin[x]; a[tot].flow=flow; lin[x]=tot;
    }
    
    queue<int> q;
    
    bool bfs() {
        memset(dep,0,sizeof(dep));
        while(!q.empty()) q.pop();
        dep[s]=1;
        q.push(s);
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=lin[u];i;i=a[i].next)
            {
                int v=a[i].y;
                if(!dep[v]&&a[i].flow) 
                {
                    dep[v]=dep[u]+1;
                    q.push(v);
                    if(v==t) return true;
                }
            }
        }
        return false;
    }
    
    int dinic(int x,int t,int limit) {
        if(x==t) return limit;
        int flow=0,k;
        for(int i=lin[x];i;i=a[i].next) {
            int v=a[i].y;
            if(dep[v]==dep[x]+1&&a[i].flow&&(k=dinic(v,t,min(limit,a[i].flow)))) {
                flow+=k;
                limit-=k;
                a[i].flow-=k;
                a[i^1].flow+=k;
                if(!flow) break;
            }
        }
        return flow;
    }
    
    int main()
    {
        int i,j,num=0;
        read(n); read(m); read(d);
        for(i=1;i<=n;i++) {
            scanf("%s",s1[i]); 
            for(j=0;j<m;j++) {
                hi[i][j+1]=s1[i][j]-48;
                if(hi[i][j+1]!=0) 
                    k++,X[k]=i,Y[k]=j+1,zn[i][j+1]=k;
            }
        }
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
            if(hi[i][j]!=0) {
                    if(i<=d||i+d>n||j<=d||j+d>m)
                        add(zn[i][j]+k,2*k+1,inf),add(2*k+1,zn[i][j]+k,0);//2*k+1为超级汇点,可以跳出去就连边
                }
        for(i=1;i<=n;i++) {
            scanf("%s",s2[i]);
            for(j=0;j<m;j++)
            if(s2[i][j]=='L') {
                int v=zn[i][j+1];
                num++;
                add(0,v,1); 
                add(v,0,0);
            }
        }
        for(i=1;i<=k;i++)
            add(i,i+k,hi[X[i]][Y[i]]),add(i+k,i,0);
        for(i=1;i<=k;i++)
            for(j=1;j<=k;j++) {
                if(i==j) continue;
                if((X[i]-X[j])*(X[i]-X[j])+(Y[i]-Y[j])*(Y[i]-Y[j])<=d*d) 
                    add(i+k,j,inf),add(j,i+k,0); 
            }
        s=0;t=2*k+1;
        int max_flow=0;
        while(bfs()) max_flow+=dinic(s,t,inf);
        printf("%d",num-max_flow); 
    }
    View Code

    星际战争

    这个二分我真的;用的实数二分,都成上10000,最后除,题目允许误差;

    很显然答案就有单调性,我们可以二分一个时间t,如果在t时间,伤害值为hurt*t,源点向武器连边,容量为伤害值,武器和装甲之间连边,容量为inf,装甲和汇点连边

    容量为装甲值;跑最大流判定是否等于装甲值之和;

    #include<bits/stdc++.h>
    using namespace std;
    #define inf 1e11
    #define ll long long
    template<typename T>inline void read(T &x)
    {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    
    ll n,m,tot=1,ss,tt;
    ll hp[60],hurt[60],lin[200],d[200],X[60][60];
    
    struct gg {
        ll x,y,next,flow;
    }a[500001];
    
    inline void add(ll x,ll y,ll flow) {
        a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; a[tot].flow=flow;
    }
    
    inline void build(ll t) {
        for(int i=1;i<=m;i++) {
            add(ss,i,hurt[i]*t);
            add(i,ss,0);
        }
        for(int i=1;i<=n;i++) {
            add(i+m,tt,hp[i]);
            add(tt,i+m,0);
        }
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                if(X[i][j])
                    add(i,j+m,inf),add(j+m,i,0) ;
    }
    
    queue<int> q;
    
    inline bool bfs() {
        memset(d,0,sizeof(d));
        while(!q.empty()) q.pop();
        q.push(ss); d[ss]=1;
        while(q.size()) {
            int x=q.front(); q.pop();
            for(int i=lin[x];i;i=a[i].next) {
                int y=a[i].y;
                if(a[i].flow&&!d[y]) {
                    d[y]=d[x]+1;
                    q.push(y);
                    if(y==tt) return true;
                }
            }
        }
        return false;
    }
    
    
    inline ll dinic(ll x,ll flow) {
        if(x==tt) return flow;
        ll rest=flow,k;
        for(int i=lin[x];i&&rest;i=a[i].next) {
            ll y=a[i].y;
            if(a[i].flow&&d[y]==d[x]+1) {
                k=dinic(y,min(rest,a[i].flow));
                if(!k) d[y]=0;
                a[i].flow-=k;
                a[i^1].flow+=k;
                rest-=k;
            }
        }
        return flow-rest;
        
    }
    
    inline ll check() {
        ll max_flow=0;
        while(bfs()) max_flow+=dinic(ss,inf);
        return max_flow;
    }
    
    int main() {
        read(n); read(m);
        ll sum=0;
        ss=0,tt=n+m+1;
        for(int i=1;i<=n;i++) {
            read(hp[i]); hp[i]*=10000; sum+=hp[i];
        }
        for(int i=1;i<=m;i++) {
            read(hurt[i]);
        }
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                read(X[i][j]);
        ll l=0,r=sum; 
        while(l<r) {
            ll mid=(l+r)>>1;
            tot=1;
            memset(lin,0,sizeof(lin));
            build(mid);
            ll ans=check();
            if(ans>=sum) r=mid;
            else l=mid+1;
        }
        printf("%.6lf",l*1.0/10000);
        return 0;
    } 
    View Code

    文理分科

    首先这是个最小割,最小割等于最大流;

    任意一个同学和源点相连,容量为art[i][j],在和汇点相连,容量为science[i][j],求最小割,割掉理科容量即选择文科,文科也是;

    那么考虑处理same_art/same_science的情况,我们可以新建一个节点,源点和它相连,容量为same_art,那么他需要和另外五个点,(与他相邻和它本身)相连,容量为inf。

    因为走这条边一定要走另外五条;对于same_science,我们将其连向汇点,同样连五个点;

    跑最大流,总数减去就好,注意反向边的容量为0;

    #include<bits/stdc++.h>
    using namespace std;
    #define N 500001
    #define inf 1e9
    template<typename T>inline void read(T &x)
    {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    
    int dx[]={0,0,0,1,-1};
    int dy[]={0,-1,1,0,0};
    
    int n,m,tot=1,ss,tt,lin[N],d[N],art[501][501],science[501][501],same_art[501][501],same_science[501][501];
    
    struct gg {
        int y,next,flow;
    }a[2000001];
    
    inline void add(int x,int y,int flow) {
        a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; a[tot].flow=flow;
    }
    
    queue<int> q;
    
    inline bool bfs() {
        memset(d,0,sizeof(d));
        while(!q.empty()) q.pop();
        q.push(ss); d[ss]=1;
        while(q.size()) {
            int x=q.front(); q.pop();
            for(int i=lin[x];i;i=a[i].next) {
                int y=a[i].y;
                if(a[i].flow&&!d[y]) {
                    d[y]=d[x]+1;
                    q.push(y);
                    if(y==tt) return true;
                }
            }
        }
        return false;
    }
    
    
    inline int dinic(int x,int flow) {
        if(x==tt) return flow;
        int rest=flow,k;
        for(int i=lin[x];i&&rest;i=a[i].next) {
            int y=a[i].y;
            if(a[i].flow&&d[y]==d[x]+1) {
                k=dinic(y,min(rest,a[i].flow));
                if(!k) d[y]=0;
                a[i].flow-=k;
                a[i^1].flow+=k;
                rest-=k;
            }
        }
        return flow-rest;
        
    }
    
    inline int num(int i,int j) {
        return (i-1)*m+j;
    }
    
    int main() {
    //    freopen("a.in","r",stdin);
    //    freopen("a.out","w",stdout);
        read(n); read(m);
        ss=0,tt=n*m+1;
        int ans=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++) {
                read(art[i][j]);
                ans+=art[i][j];
                add(ss,num(i,j),art[i][j]);
                add(num(i,j),ss,0);
            }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++) {
                read(science[i][j]);
                ans+=science[i][j];
                add(num(i,j),tt,science[i][j]);
                add(tt,num(i,j),0);
            }
        int cnt=tt;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++) {
                read(same_art[i][j]);
                ans+=same_art[i][j];
                add(ss,++cnt,same_art[i][j]);
                add(cnt,ss,0);
                for(int k=0;k<5;k++) {
                    int xx=i+dx[k],yy=j+dy[k];
                    if(xx<1||xx>n||yy<1||yy>m) continue;
                    add(cnt,num(xx,yy),inf); add(num(xx,yy),cnt,0);
                }
            }
        for(int i=1;i<=n;i++) 
            for(int j=1;j<=m;j++) {
                read(same_science[i][j]);
                ans+=same_science[i][j];
                add(++cnt,tt,same_science[i][j]);
                add(tt,cnt,0);
                for(int k=0;k<5;k++) {
                    int xx=i+dx[k],yy=j+dy[k];
                    if(xx<1||xx>n||yy<1||yy>m) continue;
                    add(num(xx,yy),cnt,inf); add(cnt,num(xx,yy),0);
                }
            }
        
        int max_flow=0,flow=0;
        while(bfs()) 
            while(flow=dinic(ss,inf)) max_flow+=flow;
        printf("%d
    ",ans-max_flow);
        return 0;
    }
    View Code

    最小割

    这个题目是让求可行边和必须边;

    一般流程:不说证明;

    dinic求最大流,tarjan跑强连通分量(忽略此时流量为0的边,我的理解是此时两边不连通,对于连通的定义就是两点之间的边还有容量,那么已经被阻隔,不能再走);

    如果不是一个强连通分量,是可行边,在可行边里找必须边,当且仅当源点和x是一个强连通分量,y和汇点是一个强连通分量;

    #include<bits/stdc++.h>
    using namespace std;
    #define N 500001
    #define inf 1e9 
    
    template<typename T>inline void read(T &x)
    {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    
    int n,m,s,t,tot=1,num,top,cnt;
    int lin[N],ins[N],dfn[N],low[N],c[N],Stack[N],d[N];
    
    struct gg {
        int x,y,next,flow;
    }a[N<<1];
    
    inline void add(int x,int y,int flow) {
        a[++tot].y=y; 
        a[tot].x=x;
        a[tot].flow=flow; 
        a[tot].next=lin[x]; 
        lin[x]=tot;
    }
    
    queue<int> q;
    
    inline bool bfs() {
        memset(d,0,sizeof(d));
        while(!q.empty()) q.pop();
        q.push(s); d[s]=1;
        while(!q.empty()) {
            int x=q.front(); q.pop();
            for(int i=lin[x];i;i=a[i].next) {
                int y=a[i].y;
                if(a[i].flow&&!d[y]) {
                    q.push(y);
                    d[y]=d[x]+1;
                    if(y==t) return 1;
                }
            }
        }
        return 0;
    }
    
    inline int dinic(int x,int flow) {
        if(x==t) return flow;
        int rest=flow,k;
        for(int i=lin[x];i&&rest;i=a[i].next) {
            int y=a[i].y;
            if(a[i].flow&&d[y]==d[x]+1) {
                k=dinic(y,min(rest,a[i].flow));
                if(!k) d[y]=0;
                a[i].flow-=k;
                a[i^1].flow+=k;
                rest-=k;
            }
        } 
        return flow-rest;
    }
    
    void tarjan(int x)
    {
        dfn[x]=low[x]=++num;
        Stack[++top]=x;
        ins[x]=1;
        for(int i=lin[x];i;i=a[i].next)
        {
            int u=a[i].y;
            if(!a[i].flow) continue;
            if(!dfn[u])
            {
                tarjan(u);
                low[x]=min(low[x],low[u]);
            }
            else if(ins[u])    low[x]=min(low[x],dfn[u]);
        }
        int k;
        if(low[x]==dfn[x])
        {
            ++cnt;
            do
            {
                k=Stack[top--];
                ins[k]=0;
                c[k]=cnt;
            }while(x!=k);
        }
    }
    
    int main(){
    //    freopen("a.in","r",stdin);
    //    freopen("a.out","w",stdout);
        read(n); read(m); read(s); read(t);
        for(int i=1,x,y,w;i<=m;i++) {
            read(x); read(y); read(w);
            add(x,y,w); add(y,x,0);
        }
        int flow=0,max_flow=0;
        while(bfs()) 
            while(flow=dinic(s,inf)) max_flow+=flow;
        for(int i=1;i<=n;i++) {
            if(!dfn[i]) tarjan(i);
        }
        for(int i=2;i<tot;i+=2) {
            int x=a[i].x,y=a[i].y;
            if(!a[i].flow&&c[x]!=c[y]) {
                printf("1 ");
                if(c[s]==c[x]&&c[y]==c[t])
                    printf("1
    ");
                else printf("0
    "); 
            }
            else printf("0 0
    ");
        }
        return 0;
    }
    View Code

    今天没写费用流,而且上下界网络流也没写,有空补上;

  • 相关阅读:
    BeautifulSoup
    requests
    安装xpath helper
    取消搜狗输入法的快捷键
    numpy初识 old
    Jupyter Notebook 快捷键
    安装numpy、matplotlib
    JavaScript 继承 -JavaScript高级程序设计
    mac /windows
    unicode 地址
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/11147838.html
Copyright © 2011-2022 走看看