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

    A.蜥蜴

    将每个石柱拆点,最多可起跳石柱的高度次,入点向出点连其高度的边。

    两个石柱平面距离不超过d时可互相到达,由一个石柱的出点向另一个石柱的入点连$Inf$边。

    建立源点和汇点。

      当前石柱有蜥蜴时,源点$s$向该石柱入点连容量为$1$的边。

      当前石柱的横或纵坐标距离边界最短距离$<d$时,该石柱出点向汇点$t$连$Inf$边。

    跑最大流就是可逃出去最多的蜥蜴数,用总蜥蜴数减去即可。

    #include <bits/stdc++.h>
    #define Inf 0x3f3f3f3f
    using namespace std;
    int n,m,d,S,t,ans;
    char s[25];
    const int maxn=805;
    int cnt=-1,head[maxn],vis[maxn];
    struct Edge{
        int to,next,w;
    }e[maxn*maxn];
    void add(int u,int v,int w) {
        e[++cnt].to=v;
        e[cnt].w=w;
        e[cnt].next=head[u];
        head[u]=cnt;
        e[++cnt].to=u;
        e[cnt].next=head[v];
        head[v]=cnt;
    }
    bool Judge(int x,int y,int xx,int yy) {
        double temp=sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
        return temp<=d;
    }
    queue<int>q;
    bool bfs() {
        q.push(S);
        memset(vis,0,sizeof(vis));
        vis[S]=1;
        while(!q.empty()) {
            int u=q.front();
            q.pop();
            for(int i=head[u];~i;i=e[i].next) {
                int v=e[i].to;
                if(e[i].w&&!vis[v]) {
                    vis[v]=vis[u]+1;
                    q.push(v);
                }
            }
        }
        return vis[t];
    }
    int dfs(int u,int flow) {
        if(u==t) return flow;
        int temp=0,w;
        for(int i=head[u];~i;i=e[i].next) {
            int v=e[i].to;
            if(e[i].w&&vis[v]==vis[u]+1) {
                w=dfs(v,min(flow-temp,e[i].w));
                e[i].w-=w,e[i^1].w+=w,temp+=w;
                if(temp==flow) return temp;
            }
        }
        if(!temp) vis[u]=0;
        return temp;
    }
    int dinic() {
        int res=0;
        while(bfs()) res+=dfs(S,Inf);
        return res;
    }
    int main() {
        memset(head,-1,sizeof(head));
        scanf("%d%d%d",&n,&m,&d);
        S=0,t=n*m*2+1;
        for(int i=1;i<=n;++i) {
            scanf("%s",s+1);
            for(int j=1;j<=m;++j) {
                if(s[j]!='0') add((i-1)*m+j,(i-1)*m+j+n*m,s[j]-'0');
            }
        }
        for(int i=1;i<=n;++i) {
            scanf("%s",s+1);
            for(int j=1;j<=m;++j) {
                if(s[j]=='L') add(S,(i-1)*m+j,1),ans++;
            }
        }
        for(int i=1;i<=n;++i) {
            for(int j=1;j<=m;++j) {
                if(i-d<1||i+d>n||j-d<1||j+d>m) add((i-1)*m+j+n*m,t,Inf);
            }
        }
        for(int i=1;i<=n;++i) {
            for(int j=1;j<=m;++j) {
                for(int u=1;u<=n;++u) {
                    for(int v=1;v<=m;++v) {
                        if(Judge(i,j,u,v)) add((i-1)*m+j+n*m,(u-1)*m+v,Inf);
                    }
                }
            }
        }
        printf("%d
    ",ans-dinic());
        return 0;
    }
    View Code

    B.星际战争

    每个激光武器向可攻击的机器人连一条$Inf$边。

    机器人向汇点连自己的装甲值。

    二分答案 每次二分需要的时间$t$,重建汇点向每个激光武器连$B[i] imes t$容量的边,检验最后$A[i]$是否满流。

    处理精度问题可用$long double$,或者因为题目要求精度为$10^{-3}$可将其$ imes 1000$,最后输出答案时除回去即可。

    邻接表清空时不仅$cnt$和$head$数组要初始化,链表$e$也要重置

    #include<bits/stdc++.h>
    #define Inf 0x3f3f3f3f3f3f3f3f
    using namespace std;
    const int maxn=50+5;
    int n,m,a[maxn],b[maxn],s,t;
    bool mapp[maxn][maxn];
    int cnt=-1,head[maxn<<1],vis[maxn<<1];
    typedef long long ll;
    ll sum;
    struct Edge{
        int to,next;
        ll w;
    }e[maxn*maxn<<1];
    void add(int u,int v,ll w) {
        e[++cnt].to=v;
        e[cnt].w=w;
        e[cnt].next=head[u];
        head[u]=cnt;
        e[++cnt].to=u;
        e[cnt].next=head[v];
        head[v]=cnt;
    }
    queue<int>q;
    bool bfs() {
        q.push(s);
        memset(vis,0,sizeof(vis));
        vis[s]=1;
        while(!q.empty()) {
            int u=q.front();
            q.pop();
            for(int i=head[u];~i;i=e[i].next) {
                int v=e[i].to;
                if(e[i].w&&!vis[v]) {
                    vis[v]=vis[u]+1;
                    q.push(v);
                }
            }
        }
        return vis[t];
    }
    ll dfs(int u,ll flow) {
        if(u==t) return flow;
        ll temp=0,w;
        for(int i=head[u];~i;i=e[i].next) {
            int v=e[i].to;
            if(e[i].w&&vis[v]==vis[u]+1) {
                w=dfs(v,min(flow-temp,e[i].w));
                e[i].w-=w,e[i^1].w+=w,temp+=w;
                if(flow==temp) return flow;
            }
        }
        if(!temp) vis[u]=0;
        return temp;
    }
    ll dinic() {
        ll res=0;
        while(bfs()) res+=dfs(s,Inf);
        return res;
    }
    bool Judge(ll mid) {
        cnt=-1;
        memset(head,-1,sizeof(head));
        memset(e,0,sizeof(e));
        for(int i=1;i<=n;++i) add(i+m,t,a[i]);
        for(int i=1;i<=m;++i)
            for(int j=1;j<=n;++j)
                if(mapp[i][j]) add(i,j+m,Inf);
        for(int i=1;i<=m;++i) add(s,i,mid*b[i]);
        return dinic()>=sum;
    }
    int main() {
        scanf("%d%d",&n,&m);
        s=0,t=n+m+1;
        for(int i=1;i<=n;++i) scanf("%d",&a[i]),a[i]=a[i]*1000,sum+=a[i];
        for(int i=1;i<=m;++i) scanf("%d",&b[i]);
        for(int i=1;i<=m;++i)
            for(int j=1;j<=n;++j)
                scanf("%d",&mapp[i][j]);
        ll l=0,r=5e9,mid;
        while(l<=r) {
            mid=l+r>>1LL;
            if(Judge(mid)) r=mid-1;
            else l=mid+1;
        }
        printf("%lf
    ",(double)l/1000);
        return 0;
    }
    View Code

    D.士兵占领

    源点连行坐标,汇点连纵坐标。

    反向思考,边容量设置为其最多可不放置的士兵数,假设最初所有能放置士兵的位置先都放置士兵。

    行列无障碍,建立一条容量为$1$的边。

    跑出的最大流就是在符合题意的前提下,最多能不放置的士兵数。

    #include <bits/stdc++.h>
    #define Inf 0x3f3f3f3f
    using namespace std;
    const int maxn=100+5;
    int m,n,k,s,t,hang[maxn],lie[maxn],x,y;
    bool mapp[maxn][maxn];
    int cnt=-1,head[maxn<<1],vis[maxn<<1];
    struct Edge{
        int to,next,w;
    }e[maxn*maxn<<1];
    void add(int u,int v,int w) {
        e[++cnt].to=v;
        e[cnt].w=w;
        e[cnt].next=head[u];
        head[u]=cnt;
        e[++cnt].to=u;
        e[cnt].next=head[v];
        head[v]=cnt;
    }
    queue<int>q;
    bool bfs() {
        memset(vis,0,sizeof(vis));
        q.push(s);
        vis[s]=1;
        while(!q.empty()) {
            int u=q.front();
            q.pop();
            for(int i=head[u];~i;i=e[i].next) {
                int v=e[i].to;
                if(e[i].w&&!vis[v]) {
                    vis[v]=vis[u]+1;
                    q.push(v);
                }
            }
        }
        return vis[t];
    }
    int dfs(int u,int flow) {
        if(u==t) return flow;
        int temp=0,w;
        for(int i=head[u];~i;i=e[i].next) {
            int v=e[i].to;
            if(e[i].w&&vis[v]==vis[u]+1) {
                w=dfs(v,min(flow-temp,e[i].w));
                e[i].w-=w,e[i^1].w+=w,temp+=w;
                if(temp==flow) return temp;
            }
        }
        if(!temp) vis[u]=0;
        return temp;
    }
    int dinic() {
        int res=0;
        while(bfs()) res+=dfs(s,Inf);
        return res;
    }
    int main() {
        s=0,t=n+m+1;
        memset(head,-1,sizeof(head));
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;++i) scanf("%d",&hang[i]),hang[i]=m-hang[i];
        for(int i=1;i<=m;++i) scanf("%d",&lie[i]),lie[i]=n-lie[i];
        int flag=0;
        for(int i=1;i<=k;++i) {
            scanf("%d%d",&x,&y);
            mapp[x][y]=1;
            hang[x]--,lie[y]--;
            if(hang[x]<0||lie[x]<0) flag=1;
        }
        if(flag) printf("JIONG!
    ");
        else {
            for(int i=1;i<=n;++i) add(s,i,hang[i]);
            for(int i=1;i<=m;++i) add(i+n,t,lie[i]);
            for(int i=1;i<=n;++i) {
                for(int j=1;j<=m;++j) {
                    if(!mapp[i][j]) add(i,j+n,1);
                }
            }
        }
        printf("%d
    ",n*m-k-dinic());
        return 0;
    }
    View Code

    H.最大获利

    最大权闭合子图简单模型。

    每个用户群设置一个节点,源点$s$向其连可获得收益为$w[i]$的边,节点向两个所需的中转站连$Inf$边。

    每个中转站向汇点$t$连建立它所需的费用$p[i]$的边。

    初始设置服务所有用户群,$sum=$获利$w[i]$的总和,在上图中跑最小割。

    割边不会为$Inf$边,那么就只会为$w[i]$或$p[i]$的边:

      割$w[i]$时代表失去该用户群;

      反之保留该用户群,花费对应的$p[i]$建立中转站。

    答案为$sum-$最小割。

    #include <bits/stdc++.h>
    #define Inf 0x3f3f3f3f
    using namespace std;
    const int maxn=6e4+5;
    int n,m,x,y,z,s,t,ans;
    int cnt=-1,head[maxn],vis[maxn];
    struct Edge{
        int to,next,w;
    }e[maxn*6];
    void add(int u,int v,int w) {
        e[++cnt].to=v;
        e[cnt].w=w;
        e[cnt].next=head[u];
        head[u]=cnt;
        e[++cnt].to=u;
        e[cnt].next=head[v];
        head[v]=cnt;
    }
    queue<int>q;
    bool bfs() {
        memset(vis,0,sizeof(vis));
        q.push(s);
        vis[s]=1;
        while(!q.empty()) {
            int u=q.front();
            q.pop();
            for(int i=head[u];~i;i=e[i].next) {
                int v=e[i].to;
                if(e[i].w&&!vis[v]) {
                    vis[v]=vis[u]+1;
                    q.push(v);
                }
            }
        }
        return vis[t];
    }
    int dfs(int u,int flow) {
        if(u==t) return flow;
        int temp=0,w;
        for(int i=head[u];~i;i=e[i].next) {
            int v=e[i].to;
            if(e[i].w&&vis[v]==vis[u]+1) {
                w=dfs(v,min(flow-temp,e[i].w));
                e[i].w-=w,e[i^1].w+=w,temp+=w;
                if(flow==temp) return temp;
            }
        }
        if(!temp) vis[u]=0;
        return temp;
    }
    int dinic() {
        int res=0;
        while(bfs()) res+=dfs(s,Inf);
        return res;
    }
    int main() {
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        s=0,t=n+m+1;
        for(int i=1;i<=n;++i) scanf("%d",&x),add(i,t,x);
        for(int i=1;i<=m;++i) scanf("%d%d%d",&x,&y,&z),add(i+n,x,Inf),add(i+n,y,Inf),add(s,i+n,z),ans+=z;
        printf("%d
    ",ans-dinic());
        return 0;
    }
    View Code
  • 相关阅读:
    1082 射击比赛 (20 分)
    1091 N-自守数 (15 分)
    1064 朋友数 (20 分)
    1031 查验身份证 (15 分)
    1028 人口普查 (20 分)
    1059 C语言竞赛 (20 分)
    1083 是否存在相等的差 (20 分)
    1077 互评成绩计算 (20 分)
    792. 高精度减法
    791. 高精度加法
  • 原文地址:https://www.cnblogs.com/1999-09-21Karry-erfs/p/13962400.html
Copyright © 2011-2022 走看看