zoukankan      html  css  js  c++  java
  • 【刷题记录】网络流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');
        }
    }
  • 相关阅读:
    26 转义符 re模块 方法 random模块 collection模块的Counter方法
    25 正则表达式
    24 from 模块 import 名字
    24 from 模块 import 名字
    24 from 模块 import 名字
    23 析构方法 items系列 hash方法 eq方法
    21 isinstance issubclass 反射 _str_ _new_ _len_ _call_
    20 属性, 类方法, 静态方法. python2与python3的区别.
    python(1)
    python之字符串格式化
  • 原文地址:https://www.cnblogs.com/akonoh/p/10220051.html
Copyright © 2011-2022 走看看