ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

网络流24题

2021-07-17 09:30:38  阅读:137  来源: 互联网

标签:24 vol last int 网络 dep include first


网络流24题 1/24 洛谷P2756 飞行员配对方案问题(二分图匹配)

题目思路

二分图匹配的板子题,用dinic求解时,先将源点连所有左部点,汇点连所有右部点,容量全设为1,然后连上所有题目给出的边,这样就建好图了。
跑dinic后最大流就是二分图最大匹配值。
题目还要求输出左部和右部点相连情况,开始在想用一个fa数组记录连接情况,每次dfs更新,但是wa了3、4个点,估计是撤销操作的影响。其实只要判断左部点连向右部的正向边是否为0就好,为零输出两个端点。

网络流24题 2/24 洛谷P3254 圆桌问题(二分图多重匹配)

题目思路

建图基本跟上题一样,只是源点到左部点,右部点到汇点的容量变一下就行了。
题目要求判断是否成立,成立就输出所有干部的就餐位置
我们让餐桌作为左部点,单位表示右部点,我们最后只需要判断右部点到汇点边的容量是否都为0,都为0说明说明成立。
然后我们判断就餐位置就跟上题类似,判断右部点连向左部点的边是否为1,为1说明正边被走过,输出左部点。
很坑的是这题是先输入m再输入n,要注意这里。

ac代码

int cnt=1;
struct node
{
    int to,next;
    int w;
}e[maxn];
int first[maxn];
int n,m,s,t;
int dep[maxn];
int cur[maxn];
int fa[maxn];
int vis[maxn];

void add(int u,int v,int w)
{
    e[++cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=first[u];
    first[u]=cnt;
}

inline bool bfs()
{
    ms(dep,-1);
    dep[s]=0;
    memcpy(cur,first,sizeof(first));
    queue<int>q;
    q.push(s);

    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=first[u];i!=-1;i=e[i].next)
        {
            int to=e[i].to;
            int vol=e[i].w;
            if(vol>0&&dep[to]==-1)
            {
                dep[to]=dep[u]+1;
                q.push(to);
            }
        }
    }
    return dep[t]!=-1;
}

inline int dfs(int u=s,int flow=inf)
{

    if(u==t)
        return flow;
    int last=flow;
    for(int i=cur[u];i!=-1&&last;i=e[i].next)
    {
        cur[u]=i;
        int to=e[i].to;
        int vol=e[i].w;

        if(vol>0&&dep[to]==dep[u]+1)
        {
            int c=dfs(to,min(vol,last));
            fa[u]=to;
            last-=c;
            e[i].w-=c;
            e[i^1].w+=c;

        }else
        {
            fa[u]=-1;
        }
    }
    return flow-last;
}
inline void dinic()
{

    while(bfs())
    {
        dfs();
    }
    int flag=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=first[i];j!=-1;j=e[j].next)
        {
            if(e[j].to!=t)continue;
            if(e[j].w!=0)
            {
                flag=1;
            }
        }
    }
    if(flag==1)printf("0\n");
    else
    {
        printf("1\n");
        for(int i=1;i<=n;i++)
        {

            for(int j=first[i];j!=-1;j=e[j].next)
            {
                if(e[j].to==t)continue;
                if(e[j].w==1)
                {
                    printf("%d ",e[j].to-n);
                }
            }
            printf("\n");
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    s=0,t=m+n+1;
    ms(first,-1);
    ms(fa,-1);
    for(int i=1;i<=n;i++)
    {
        int w;
        scanf("%d",&w);
        add(i,t,w);
        add(t,i,0);
    }

    for(int i=1;i<=m;i++)
    {
        int w;
        scanf("%d",&w);
        add(i+n,s,0);
        add(s,i+n,w);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            add(i,j+n,0);
            add(j+n,i,1);
        }
    }
    dinic();

}

网络流24题 3/24 洛谷P2763 试题库问题(二分图多重匹配)

题目思路

基本思路跟上题一样,但是建图的时候源点连向题目的边容量应该为1
这里没考虑清楚,wa了一发

网络流24题 4/24 洛谷P2764 最小路径覆盖问题(最小路径不相交覆盖)

题目思路

将每个点拆成两份x1,x2,源点连向x1,x2连向汇点,容量均为1。
在根据题目给的边,将端点连起来,容量也为1。
我们对这个图跑最短路,跑出来的最大流就是原图中点的最大匹配值
又因为最小路径覆盖=原图节点数-新图最大匹配数
所以我们就能够的到答案了。
输出路径见代码

ac代码


int cnt=1;
struct node
{
    int to,next;
    int w;
}e[maxn];
int first[maxn];
int n,m,s,t;
int dep[maxn];
int cur[maxn];

int vis[maxn];

void add(int u,int v,int w)
{
    e[++cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=first[u];
    first[u]=cnt;
}

inline bool bfs()
{
    ms(dep,-1);
    dep[s]=0;
    memcpy(cur,first,sizeof(first));
    queue<int>q;
    q.push(s);

    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=first[u];i!=-1;i=e[i].next)
        {
            int to=e[i].to;
            int vol=e[i].w;
            if(vol>0&&dep[to]==-1)
            {
                dep[to]=dep[u]+1;
                q.push(to);
            }
        }
    }
    return dep[t]!=-1;
}

inline int dfs(int u=s,int flow=inf)
{

    if(u==t)
        return flow;
    int last=flow;
    for(int i=cur[u];i!=-1&&last;i=e[i].next)
    {
        cur[u]=i;
        int to=e[i].to;
        int vol=e[i].w;

        if(vol>0&&dep[to]==dep[u]+1)
        {
            int c=dfs(to,min(vol,last));

            last-=c;
            e[i].w-=c;
            e[i^1].w+=c;

        }
    }
    return flow-last;
}
inline void dinic()
{
    int ans=0;

    while(bfs())
    {
        ans+=dfs();
    }


    for(int i=1;i<=n;i++)vis[i]=0;
    for(int i=1;i<=n;i++)
    {
        if(vis[i]==1)continue;
        int x=i;
        int flag=1;
        while(1)
        {
            if(flag==0)break;
            flag=0;
            printf("%d ",x);
            vis[x]=1;
            for(int j=first[x];j!=-1;j=e[j].next)
            {
                int to=e[j].to;
                if(e[j].w==0&&vis[to]==0&&to!=s)
                {
                    x=to-n;
                    flag=1;
                    break;
                }
            }
        }
        printf("\n");
    }
    printf("%d\n",n-ans);


}

int main()
{
    scanf("%d%d",&n,&m);
    s=0,t=2*n+1;
    ms(first,-1);
    for(int i=1;i<=n;i++)
    {
        add(s,i,1);
        add(i,s,0);
        add(i+n,t,1);
        add(t,i+n,0);
    }
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v+n,1);
        add(v+n,u,0);
    }
    dinic();

}



网络流24题 5/24 洛谷P2765 魔术球问题

题目思路

题目乍一看会有贪心等想法,其实确实也有这种解法,但作为网络流24题中的一个,还是用网络流求解。
因为题目给出要求每个柱子上的点之间和为完全平方数,所以我们可以根据这个条件建边。
题目要求按编号从小到大将球放上柱子,那么我们就枚举球的编号。每次加入后,将球连向源点汇点,再将已经放入的能和它组成完全平方数的点连向它。然后我们每次枚举建完图后都跑一边dinic,当我们得不到值(即该点加入后没有新的匹配生成)时我们将这个点放到新的柱子的第一位。如果柱子数大于题目给的n,那么说明当前值无法放入了,直接退出枚举。我们就得到了需要的值。
这题建议不要用memset来初始化,不然会t。
还有不用当前弧优化能过,用memcpy来进行弧优化的初始化会t,就很神奇。

ac代码

#pragma comment(linker, "/STACK:102400000,102400000")
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1h
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pb(x) push_back(x)
using namespace std;
const int maxn = 2e6+10;
const int inf = 1e9;
const ll llinf =1e18+10;
const ll mod = 1e9+7;


int cnt=1;
struct node
{
    int to,next;
    int w;
}e[maxn];
int first[maxn];
int n,m,s,t;
int dep[maxn];
int cur[maxn];
int top[maxn];
int sp[maxn];
int now,tot;

void add(int u,int v,int w)
{
    e[++cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=first[u];
    first[u]=cnt;
}

inline bool bfs()
{
    for(int i=1;i<=100010;i++)dep[i]=-1;
    dep[s]=0;
    queue<int>q;
    q.push(s);

    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=first[u];i;i=e[i].next)
        {
            int to=e[i].to;
            int vol=e[i].w;
            if(vol>0&&dep[to]==-1)
            {
                dep[to]=dep[u]+1;
                q.push(to);
            }
        }
    }
    return dep[t]!=-1;
}

inline int dfs(int u=s,int flow=inf)
{

    if(u==t)
        return flow;
    int last=flow;
    for(int i=first[u];i&&last;i=e[i].next)
    {
        //cur[u]=i;
        int to=e[i].to;
        int vol=e[i].w;

        if(vol>0&&dep[to]==dep[u]+1)
        {
            int c=dfs(to,min(vol,last));

            last-=c;
            e[i].w-=c;
            e[i^1].w+=c;

        }
    }
    return flow-last;
}
inline int dinic()
{
    int ans=0;

    while(bfs())
    {
        ans+=dfs();
    }


    return ans;


}

int main()
{
    int n;
    scanf("%d",&n);

    s=0;
    t=100*1000+1;

    tot=0,now=0;
    while(tot<=n)
    {
        ++now;

        add(s,now*2,1);
        add(now*2,s,0);
        add(now*2+1,t,1);
        add(t,now*2+1,0);

        for(int i=sqrt(now);i*i<2*now;i++)
        {
            if(i*i<=now)continue;
            int tem=i*i-now;
            add(tem*2,now*2+1,1);
            add(now*2+1,tem*2,0);
        }
        int tem=dinic();
        if(tem==0)
            top[++tot]=now;
    }
    printf("%d\n",now-1);
    for(int i=1;i<=n;i++)
    {
        int x=top[i];
        while(1)
        {
            printf("%d ",x);
            int flag=0;
            for(int j=first[2*x];j;j=e[j].next)
            {
                int to=e[j].to;
                if(to==s)continue;
                if(e[j].w==0)
                {
                    x=(to-1)/2;
                    flag=1;
                    break;
                }

            }
            if(flag==0)
                break;

        }
        printf("\n");
    }
}

总结

这题开始想的时候枚举建图的方式都搞错了,还是要记住二分图建图的模型和它代表的意义,不要跟别普通的图搞混。
尽量少用memset,几组数据都是删前tle,删完之后只有30ms

网络流24题 6/24 洛谷P2766 最长不下降子序列问题

题目思路

题目给出了三问。
因为数据较小,所有第一问可以用n方dp求出答案s。
对于第二问,第三问我们用网络流求解。
首先对于第二问,每个点都只出现在一个序列里面,所以考虑拆点。
我们将点i拆成i_a和i_b两个点,i_a连向i_b并且容量为1。
我们可以根据dp的结果来决定其他的连线
我们首先让dp[i]==1的点i跟s连一条边,容量为1,s流向i。
然后让dp[i]==s的点i跟t连一条边,容量为1,i流向t。
然后对满足i<j,a[i]<a[j],dp[j]=dp[i]+1的i、j两点连边。
这样我们就建好图了,跑完最短路就能得到第二问的答案。
第三问跟第二问类似,只需要把s连向1和n,1内部连线,n内部连线,1和n连向t的边容量全部变成inf就好了。但是这样会出现一个特殊情况,就是s = = 1时,图的结果会是inf,而不是答案1。所以我们需要特判这个情况。

ac代码

#pragma comment(linker, "/STACK:102400000,102400000")
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1h
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pb(x) push_back(x)
using namespace std;
const int maxn = 2e6+10;
const int inf = 0x7f7f7f7f;
const ll llinf =1e18+10;
const ll mod = 1e9+7;


int cnt=1;
struct node
{
    int to,next;
    int w;
}e[maxn];
int first[maxn];
int n,m,s,t;
int dep[maxn];
int cur[maxn];
int top[maxn];
int dp[maxn];
int a[maxn];
int now,tot;

void add(int u,int v,int w)
{
    e[++cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=first[u];
    first[u]=cnt;
}

inline bool bfs()
{
    ms(dep,-1);
    dep[s]=0;
    queue<int>q;
    q.push(s);

    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=first[u];i;i=e[i].next)
        {
            int to=e[i].to;
            int vol=e[i].w;
            if(vol>0&&dep[to]==-1)
            {
                dep[to]=dep[u]+1;
                q.push(to);
            }
        }
    }
    return dep[t]!=-1;
}

inline int dfs(int u=s,int flow=inf)
{

    if(u==t)
        return flow;
    int last=flow;
    for(int i=first[u];i&&last;i=e[i].next)
    {
        //cur[u]=i;
        int to=e[i].to;
        int vol=e[i].w;

        if(vol>0&&dep[to]==dep[u]+1)
        {
            int c=dfs(to,min(vol,last));

            last-=c;
            e[i].w-=c;
            e[i^1].w+=c;

        }
    }
    return flow-last;
}

inline int dinic()
{
    int ans=0;

    while(bfs())
    {
        //print1;
        ans+=dfs();
    }


    return ans;


}

int main()
{
    scanf("%d",&n);
    s=0,t=n*2+1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    int ss=0;
    for(int i=1;i<=n;i++)dp[i]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<i;j++)
        {
            if(a[i]>=a[j])
                dp[i]=max(dp[i],dp[j]+1);

        }
        ss=max(ss,dp[i]);
    }
    printf("%d\n",ss);
    for(int i=1;i<=n;i++)
    {
        add(i,i+n,1);
        add(i+n,i,0);
        if(dp[i]==1)
        {
            add(s,i,1);
            add(i,s,0);
        }
        if(dp[i]==ss)
        {
            add(i+n,t,1);
            add(t,i+n,0);
        }
        for(int j=i+1;j<=n;j++)
        {
            if(a[i]<=a[j]&&dp[j]==dp[i]+1)
            {
                add(i+n,j,1);
                add(j,i+n,0);
            }
        }
    }
    int ans=dinic();
    printf("%d\n",ans);
    cnt=1;
    if(ss==1)
    {
        set<int>st;
        for(int i=1;i<=n;i++)
            st.insert(a[i]);
        printf("%d\n",st.size());
    }else
    {
        ms(first,0);
        for(int i=1;i<=n;i++)
        {
            if(dp[i]==1)
            {
                if(i==1||i==n)
                {
                    add(s,i,inf);
                    add(i,s,0);
                }else
                {
                    add(s,i,1);
                    add(i,s,0);
                }
            }
            if(dp[i]==ss)
            {
                if(i==n||i==1)
                {
                    add(i+n,t,inf);
                    add(t,i+n,0);
                }else
                {
                    add(i+n,t,1);
                    add(t,i+n,0);
                }
            }
            if(i==1||i==n)
            {
                add(i,i+n,inf);
                add(i+n,i,0);
            }else
            {
                add(i,i+n,1);
                add(i+n,i,0);
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                if(a[i]<=a[j]&&dp[j]==dp[i]+1)
                {
                    add(i+n,j,1);
                    add(j,i+n,0);
                }
            }
        }
        int ans=dinic();
        printf("%d\n",ans);
    }
}

标签:24,vol,last,int,网络,dep,include,first
来源: https://blog.csdn.net/daydreamer23333/article/details/118767513

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有