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\n",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\n");
            else printf("0\n"); 
        }
        else printf("0 0\n");
    }
    return 0;
}
View Code

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

 

posted @ 2019-07-07 21:07  Tyouchie  阅读(184)  评论(0编辑  收藏  举报