• 【刷题记录】网络流24题等


    令人崩溃的五道题

    首先是网络流24题中的前五题

    1.飞行员配对问题

    标准的二分图匹配,这里采用时间复杂度最优秀O(sqrt(E)V)的网络流做法

    建立源点s汇点t分别连接至二分图的两个部分

    所有边权设置为1

    跑最大流即可

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    #include <queue>
    #include <cstring>
    using namespace std;
    #define N 105
    #define next nico
    int head[N],to[N*N*2],next[N*N*2],dis[N*N*2],tot=1,d[N],s=0,t,n,m;
    void add(int x,int y, int z)
    {
        to[++tot]=y;
        dis[tot]=z;
        next[tot]=head[x];
        head[x]=tot;
    }
    int bfs()
    {
        memset(d,0,sizeof(d));
        queue <int> q;
        d[s]=1;
        q.push(s);
        while(!q.empty())
        {
            int x = q.front();
            q.pop();
            for(int i = head[x]; i; i = next[i])
            {
                int des = to[i];
                if(dis[i]&&!d[des])
                {
                    d[des]=d[x]+1;
                    q.push(des);
                }
            }
        }
        return d[t];
    }
    int dfs(int x,int v)
    {
        if(x==t||v==0)return v;
        int ans = 0;
        for(int i = head[x]; i ; i = next[i])
        {
            int des = to[i];
            if(d[des]==d[x]+1)
            {
                int f = dfs(des,min(dis[i],v));
                v -= f;
                dis[i]-=f;
                dis[i^1]+=f;
                ans += f;
            }
        }
        return ans;
    }
    int main()
    {
        scanf("%d%d",&m,&n);
        int x=0,y=0;
        while(x!=-1)
        {
            scanf("%d%d",&x,&y);
            add(x,y,1);
            add(y,x,0);
        }
        for(int i = 1; i <= m; i ++)
        {
            add(n+2,i,1);
            add(i,n+2,0);
        }
        for(int i = m +1; i <= n ; i++)
        {
            add(i,n+1,1);
            add(n+1,i,0);
        }
        s=n+2;t=n+1;
        int ans = 0;
        while(bfs())
        {
            ans +=dfs(s,0x7f7f7f7f);
        }
        if(ans!=0)
        {
        printf("%d
    ",ans);
        for(int i = 1; i <= m; i ++)
        {
            for(int j = head[i];j;j=next[j])
            {
                if(dis[j]==0&&to[j]<=n)
                {
                    printf("%d %d
    ",i,to[j]);
                }
            }
        }
        }
        else puts("No Solution!");
    }

    2.太空飞行计划问题

    最大边权子图的模板题问题

    将模型转化成二分图,对于正权点连接源点,负权点连接汇点

    跑最大流即可

    答案是正权点数总和减去最大流

    选取的点是阻塞流到达的点

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cstring>
    #include <queue>
    using namespace std;
    #define N 1000
    #define next nico
    int m, n;
    const int inf = 0x0f7f7f7f;
    int head[N], next[N * N], to[N], tot = 1, val[N];
    int sum;
    void add(int x, int y, int z)
    {
        to[++tot] = y;
        next[tot] = head[x];
        val[tot] = z;
        head[x] = tot;
    }
    int d[N];
    int bfs(int s, int t)
    {
        memset(d, 0, sizeof(d));
        d[s] = 1;
        queue<int> q;
        q.push(s);
        while (!q.empty())
        {
            int x = q.front();
            q.pop();
            for (int i = head[x]; i; i = next[i])
                if (val[i] && !d[to[i]])
                {
                    d[to[i]] = d[x] + 1;
                    q.push(to[i]);
                }
        }
        return d[t];
    }
    int dfs(int x, int v, int t)
    {
        if (x == t || v == 0)
            return v;
        int ans = 0;
        for (int i = head[x]; i; i = next[i])
            if (d[x] + 1 == d[to[i]])
            {
                int l = dfs(to[i], min(val[i], v), t);
                v-=l;
                ans += l;
                val[i] -= l;
                val[i ^ 1] += l;
            }
        return ans;
    }
    
    int main()
    {
        scanf("%d%d", &m, &n);
        char buf[10001];
        cin.getline(buf, 10000);
        for (int i = 1; i <= m; i++)
        {
            memset(buf,0,sizeof(buf));
            cin.getline(buf, 10000);
            int p = 0, a;
            while (sscanf(buf + p, "%d", &a) == 1)
            {
                if (p != 0)
                {
                    add(i, a + m, inf);
                    add(a + m, i, 0);
                }
                else
                {
                    add(n + m + 1, i, a);
                    add(i, n + m + 1, 0);
                    sum+=a;
                }
                if (a == 0)
                    p++;
                else
                    while (a)
                    {
                        a /= 10;
                        p++;
                    }
                p++;
            }
        }
        for (int i = 1; i <= n; i++)
        {
            int a;
            scanf("%d", &a);
            add(m + i, n + m + 2, a);
            add(n + m + 2, m + i, 0);
        }
        int ans = 0;
        while (bfs(n + m + 1, n + m + 2))
        {
            ans += dfs(n + m + 1, inf, n + m + 2);
        }
    
        for (int j = head[n + m + 1]; j; j = next[j])
            if (d[to[j]] != 0)
            {
                printf("%d ", to[j]);
            }
        puts("");
        for (int j = head[n + m + 2]; j; j = next[j])
            if (d[to[j]]!= 0)
            {
                printf("%d ", to[j]-m);
            }
        puts("");
        printf("%d
    ", sum - ans);
    }

    3.最小路径覆盖问题

    转化成二分图匹配问题即可

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cstring>
    #include <queue>
    using namespace std;
    #define N 400
    #define next nico
    int head[N],to[30005],next[30005],tot=1,dis[30005],n,m,d[N],s,t;
    bool vis[N];
    void add(int x,int y,int z)
    {
        to[++tot]=y;
        next[tot]=head[x];
        dis[tot]=z;
        head[x]=tot;
    }
    int bfs()
    {
        memset(d,0,sizeof(d));
        queue<int> q;
        q.push(s);
        d[s]=1;
        while(!q.empty())
        {
            int x = q.front();
            q.pop();
            for(int i = head[x];i;i=next[i])
            if(dis[i]&&!d[to[i]])
            {
                d[to[i]]=d[x]+1;
                q.push(to[i]);
            }
        }
        return d[t];
    }
    int dfs(int x,int v)
    {
        if(x==t||v==0)return v;
        int ans = 0;
        for(int i = head[x];i;i = next[i])
        if(d[to[i]]==d[x]+1)
        {   
            int l = dfs(to[i],min(v,dis[i]));
            dis[i]-=l;
            dis[i^1]+=l;
            ans+=l;
            v-=l;
        }
        return ans;
    }
    void printans(int x)
    {
        vis[x]=1;
        printf("%d ",x);
        for(int i = head[x];i;i=next[i])
        {
            if(!dis[i]&&!vis[to[i]-n]&&to[i]<=n*2)
            {
                printans(to[i]-n);
            }
        }
    }
    int main()
    {
        #ifdef TEST
            freopen("test.in","r",stdin);
        #endif
        scanf("%d%d",&n,&m);
        for(int i = 1 ; i <= m ; i ++)
        {
            int a,b,c=1;
            scanf("%d%d",&a,&b);
            add(a,b+n,c);
            add(b+n,a,0);
        }
        s = 2*n+1;t = s+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);
        }
        int ans = 0;
        while(bfs())
        {
            ans += dfs(s,0x7f7f7f7f);
        }
        for(int i = 1; i <= n; i ++)
        if(!vis[i])
        {
            printans(i);
            putchar('
    ');
        }
        printf("%d",n-ans);
    }

    4.魔术球问题

    可以贪心做

    我采取将其转化成最小路径覆盖问题解决

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cstring>
    #include <queue>
    #include <map>
    #include <cmath>
    using namespace std;
    #define next nico
    #define N 5000
    #define M 2000
    int head[N],d[N],next[N*N],to[N*N],val[N*N],tot=1;
    int ans[100][N],tot1;
    int vis[N];
    void add(int a , int b ,int c)
    {
        to[++tot]=b;
        next[tot]=head[a];
        val[tot]=c;
        head[a]=tot;
    }
    int s,t;
    void getans(int x)
    {
        ans[tot1][++ans[tot1][0]]=x-2;
        vis[x-2]=1;
        for(int i = head[x];i;i=next[i])
        if(!val[i]&&to[i]>M&&!vis[to[i]-M])
        {
            getans(to[i]-M+2);
        }
    }
    int bfs()
    {
        queue <int> q;
        q.push(s);
        memset(d,0,sizeof(d));
        d[s]=1;
        while(!q.empty())
        {
            int x = q.front();
            q.pop();
            for(int i = head[x]; i; i = next[i])
            if(val[i]&&!d[to[i]])
            {
                d[to[i]]=d[x]+1;
                q.push(to[i]);
            }
        }
        return d[t];
    }
    int dfs(int x,int v)
    {
        if(x==t||v==0)return v;
        int ans = 0;
        for(int i = head[x]; i; i = next[i])
        if(d[to[i]]==d[x]+1)
        {
            int l = dfs(to[i],min(val[i],v));
            v-=l;
            ans+=l;
            val[i]-=l;
            val[i^1]+=l;
        }
        return ans;
    }
    int main()
    {
        int n,i;
        scanf("%d",&n);
        s=1;t=2;  int sum=0;
        for(i = 1; ; i ++)
        {
            add(s,i+2,1);
            add(i+2,s,0);
            add(t,i+M,0);
            add(i+M,t,1);
            for(int j = 1; j < i ; j ++)
            {
                int p = (int)sqrt((double)i+j);
                if(p*p==i+j)
                {
                    add(j+2,i+M,1);
                    add(i+M,j+2,0);
                }
            }
            while(bfs())
            {
                sum+=dfs(s,0x7f7f7f7f);
            }
            if(i-sum>n)break;
            tot1 = 0;
            memset(vis,0,sizeof(vis));
            for(int j = 1; j <= i; j ++)
            if(!vis[j])
            {
                tot1++;
                ans[tot1][0]=0;
                getans(j+2);
            }
        }
        printf("%d
    ",i-1);
        for(int i = 1 ; i<= tot1; i ++)
        {
        for(int j = 1; j <= ans[i][0];j ++)
        {
            printf("%d ",ans[i][j]);
        }
        puts("");
        }
    }

    5.圆桌问题

    与普通的二分图匹配不同,连接源点边权赋值为容纳人数

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <queue>
    #include <vector>
    #include <cstring>
    using namespace std;
    #define N 1000
    #define next nico
    int head[N],next[N*N],to[N*N],v[N*N],tot=1,s,t;
    int* val = v;
    int d[N];
    void add(int x,int y,int z)
    {
        to[++tot]=y;
        v[tot]=z;
        next[tot]=head[x];
        head[x]=tot;
    }
    int bfs()
    {
        memset(d,0,sizeof(d));
        queue<int>q;
        q.push(s);
        d[s]=1;
        while(!q.empty())
        {
            int x =q.front();
            q.pop();
            for(int i = head[x];i;i=next[i])
            if(!d[to[i]]&&v[i])
            {
                d[to[i]]=d[x]+1;
                q.push(to[i]);
            }
        }
        return d[t];
    }
    int dfs(int x,int v)
    {
        if(x==t||v==0)return v;
        int ans = 0;
        for(int i = head[x]; i ; i = next[i])
        if(d[to[i]]==d[x]+1)
        {
            int l = dfs(to[i],min(v,val[i]));
            val[i]-=l;
            val[i^1]+=l;
            v-=l;
            ans += l;
        }
        return ans ;
    }
    int main()
    {
        int m,n,sum=0;
        scanf("%d%d",&m,&n);
        s = m+n+1;
        t = s+1;
        for(int i = 1 ; i <= m; i ++)
        {
            int x;
            scanf("%d",&x);
            add(s,i,x);
            add(i,s,0);
            sum+=x;
        }
        for(int i = 1; i <= n ; i ++)
        {
            int x;
            scanf("%d",&x);
            add(i+m,t,x);
            add(t,i+m,0);
        }
        for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
        {
            add(j,i+m,1);
            add(i+m,j,0);
        }
        int ans = 0;
        while(bfs())
        {
            ans += dfs(s,0x7f7f7f7f);
        }
        if(ans == sum)
        {
            puts("1");
            for(int i = 1; i <= m; i ++)
            {
            for(int j = head[i];j;j=next[j])
            {
                if(v[j]==0&&to[j]<=m+n)
                {
                    printf("%d ",to[j]-m);
                }
            }
            puts("");
            }
        }
        else
        {
            putchar('0');
        }
    }
  • 相关阅读:
    Shell 传递参数
    Shell 变量
    Shell 教程01
    linux yum 命令
    Linux vi/vim
    Linux 磁盘管理
    你应该知道的基础 Git 命令
    Linux 下五个顶级的开源命令行 Shell
    Fedora 23如何安装LAMP服务器
    如何在Fedora或CentOS上使用Samba共享
  • 原文地址:https://www.cnblogs.com/akonoh/p/10220051.html
Copyright © 2020-2023  润新知