zoukankan      html  css  js  c++  java
  • POJ 3281 /// 最大流

    题目大意:

    n 头牛 f 种食物 d 种饮料

    每头牛有各自喜欢的食物和饮料

    求最多有多少头牛能分配到自己喜欢的食物和饮料

    因为同时有食物和饮料 所以不能用二分图匹配

     用最大流解决二分图匹配的办法

    增加一个源点连向所有食物 每头牛与各自喜欢的食物连边 

    增加一个汇点连向所有的饮料 每头牛与各自喜欢的饮料连边

    以上边容量都为1

    单纯这样连的话 一头牛可能分配到多种食物和饮料

    把一头牛拆成两个点 一点与食物连边 另一点与饮料连边

    再在两个点之间连一条容量为1的边 这样就能保证只有一个流量流过

    即只有一种食物被选 饮料反过来同理

    此时从源点到汇点跑个最大流就能得到答案

    #include <bits/stdc++.h>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int N=105;
    bool F[N][N], D[N][N];
    int n, f, d;
    
    struct EDGE { int v,c,r; };
    vector <EDGE> E[505];
    bool vis[505];
    void addE(int u,int v,int c) {
        E[u].push_back((EDGE){v,c,E[v].size()});
        E[v].push_back((EDGE){u,0,E[u].size()-1});
    }
    /**最大流*/
    int dfs(int s,int t,int f) { 
        if(s==t) return f;
        vis[s]=1;
        for(int i=0;i<E[s].size();i++) {
            EDGE& e=E[s][i]; // EDGE& 之后可直接修改到这个地址的数值
            if(!vis[e.v] && e.c>0) { // 没走过且可流过
                int d=dfs(e.v,t,min(f,e.c)); // 继续搜 能流过的最大流量
                if(d>0) { // 说明可流过d流量
                    e.c-=d; // 那么这条边的剩余容量减小
                    E[e.v][e.r].c+=d; // 反向边剩余容量变大
                    return d;
                }
            }
        }
        return 0;
    }
    int maxFlow(int s,int t) {
        int flow=0;
        while(1) {
            memset(vis,0,sizeof(vis));
            int f=dfs(s,t,INF); // 边的容量改变后 继续搜
            // 边的容量改变后就可能会走反向边 
            // 走反向边意味着 反悔正向的流量(可能有浪费)
            // 即 把正向的流量调小
            if(f==0) return flow; //  直到不可流
            flow+=f;
        }
    }
    /***/
    
    /*  0~n-1 食物一侧的牛
        n~2*n-1 饮料一侧的牛
        2*n~2*n+f-1 食物
        2*n+f~2*n+f+d-1 饮料
    */
    void solve() {
        // 增加一个源点 s=2*n+f+d
        // 增加一个汇点 t=s+1
        int s=2*n+f+d, t=s+1;
        for(int i=0;i<=t;i++) E[i].clear();
        for(int i=2*n;i<2*n+f;i++)
            addE(s,i,1); // 源点到食物
        for(int i=2*n+f;i<2*n+f+d;i++)
            addE(i,t,1); // 饮料到汇点
        for(int i=0;i<n;i++) {
            addE(i,i+n,1); // 一头牛拆成的两个点连边
            for(int j=0;j<f;j++)
                if(F[i][j]) addE(2*n+j,i,1); // 食物到牛的一点
            for(int j=0;j<d;j++)
                if(D[i][j]) addE(n+i,2*n+f+j,1); // 另一点到饮料
        }
        printf("%d
    ",maxFlow(s,t));
    }
    int main()
    {
        while(~scanf("%d%d%d",&n,&f,&d)) {
            memset(F,0,sizeof(F));
            memset(D,0,sizeof(D));
            for(int i=0;i<n;i++) {
                int a,b,t; scanf("%d%d",&a,&b);
                while(a--) {
                    scanf("%d",&t); F[i][t-1]=1;
                }
                while(b--) {
                    scanf("%d",&t); D[i][t-1]=1;
                }
            }
            solve();
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    自适应网页设计(Responsive Web Design)(转)
    PAT 1062 最简分数(20)(代码+思路)
    PAT 1059 C语言竞赛(20)(代码+思路)
    PAT 1058 选择题(20)(代码+思路)
    PAT 1057 数零壹 (20)(代码+思路)
    PAT 1054 求平均值 (20)(代码+思路+测试用例)
    PAT 1053 住房空置率 (20)(代码+思路)
    PAT 1052 卖个萌 (20)(代码+思路)
    PAT 1048 数字加密(20)(代码+思路)
    PAT 1047 编程团体赛(代码)
  • 原文地址:https://www.cnblogs.com/zquzjx/p/10122294.html
Copyright © 2011-2022 走看看