• codeforces847J Students Initiation 网络流


    题目传送门

    题意:

      有n个人,m对关系,要求每对关系中,有且仅有一个人给另外一个人送礼物,并且使送出礼物最多的人送的礼物尽可能少。并输出送礼物的方案。

    思路:这道题麻烦的是网络流模型的转换(废话)。

      最关键的因素是每对关系中只有一个人能给另外一个人送礼物,也就是说是不能相互送礼物的,虽然这句话是废话,但是正因为如此,如果我们考虑直接按照人建点,最后的方案会有问题。(流量相同,但方案错误)。

    #pragma GCC optimize (2)
    #pragma G++ optimize (2)
    #pragma comment(linker, "/STACK:102400000,102400000")
    #include<bits/stdc++.h>
    #include<cstdio>
    #include<vector>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    #define dep(i,b,a) for(int i=b;i>=a;i--)
    #define clr(a,b) memset(a,b,sizeof(a))
    #define pb push_back
    #define pii pair<int,int >
    using namespace std;
    typedef long long ll;
    const int maxn=200010;
    const int inf=0x3f3f3f3f;
    ll rd()
    {
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    const ll INFLL = 0x3f3f3f3f3f3f3f3f;
    const int INF = 0x3f3f3f3f;
    
    struct Edge {
        int to, flow, nxt;
        Edge() {}
        Edge(int to, int nxt, int flow):to(to),nxt(nxt), flow(flow) {}
    } edge[maxn << 2];
    
    int head[maxn], dep[maxn];
    int S, T;
    int N, n, m, tot,cnt;
    vector<pair<int,int> >va;
    void Init(int n) {
        N = n;
        for (int i = 0; i <= N; ++i) head[i] = -1;
        tot = 0;
    }
    
    void addv(int u, int v, int w, int rw = 0) {
        edge[tot] = Edge(v, head[u], w);
        head[u] = tot++;
        edge[tot] = Edge(u, head[v], rw);
        head[v] = tot++;
    }
    
    bool BFS() {
        for (int i = 0; i <= N; ++i) dep[i] = -1;
        queue<int>q;
        q.push(S);
        dep[S] = 1;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int i = head[u]; ~i; i = edge[i].nxt) {
                if (edge[i].flow && dep[edge[i].to] == -1) {
                    dep[edge[i].to] = dep[u] + 1;
                    q.push(edge[i].to);
                }
            }
        }
        return dep[T] < 0 ? 0 : 1;
    }
    
    int DFS(int u, int f) {
        if (u == T || f == 0) return f;
        int w, used = 0;
        for (int i = head[u]; ~i; i = edge[i].nxt) {
            if (edge[i].flow && dep[edge[i].to] == dep[u] + 1) {
                w = DFS(edge[i].to, min(f - used, edge[i].flow));
                edge[i].flow -= w;
                edge[i ^ 1].flow += w;
                used += w;
                if (used == f) return f;
            }
        }
        if (!used) dep[u] = -1;
        return used;
    }
    
    int Dicnic() {
        int ans = 0;
        while (BFS()) {
            ans += DFS(S, INF);
        }
        return ans;
    }
    int vs[5010][5010];
    bool check(int res){
        Init(T);
        rep(i,1,m){
            addv(i+n,T,1);
        }
        rep(i,1,n){
            addv(S,i,res);
        }
        rep(i,0,m-1){
            int u=va[i].first,v=va[i].second;
            addv(u,i+1+n,1);
            addv(v,i+1+n,1);
        }
        int flow=Dicnic();
        if(flow>=m){
            return true;
        }
        return false;
    }
    
    int main() {
        while (~scanf("%d %d", &n, &m)) {
            va.clear();
            S = 0, T = n+m+1;
            rep(i,1,m){
                int u,v;
                scanf("%d%d",&u,&v);
                va.push_back({u,v});
            }
            int l=0,r=n+1,mid,ans=-1;
            while(l<=r){
                mid=(l+r)>>1;
                if(check(mid)){
                    ans=mid;
                    r=mid-1;
                }else{
                    l=mid+1;
                }
            }
            
            check(ans);
            printf("%d
    ", ans);
            int flow=Dicnic();
            for(int u=1;u<=n;u++){
                for(int i=head[u];i!=-1;i=edge[i].nxt){
                    
                    if(edge[i].flow==0&&edge[i].to>n){
                        int id=edge[i].to-n;
                        int x= (va[id-1].first==u?va[id-1].second:va[id-1].first);
                            printf("%d %d
    ",u,x);
                    }
                }
            }
        }
    }

      所以我们要考虑建立n个点,每个点对应一个人,再建立m个点,每个点对应一对关系,每个关系向汇点连一条容量为1的边,每个人向自己所处的所有关系都连容量为1的边,这样就使得上面说的这个条件成立了。然后我们就建立虚拟源点,二分每个源点向人的流量,每次重新建图即可。

      最后输出方案,每个点流出的容量为0的边就是方案。

  • 相关阅读:
    http://codeforces.com/problemset/problem/594/A
    http://codeforces.com/contest/610/problem/D
    http://codeforces.com/contest/612/problem/D
    http://codeforces.com/problemset/problem/712/D
    http://codeforces.com/problemset/problem/545/D
    18/10/26 周五集训队第二次比赛补题题解
    归并排序
    hdu 2510 符号三角形
    位运算符的小总结
    hdu 1584 蜘蛛纸牌
  • 原文地址:https://www.cnblogs.com/mountaink/p/11610466.html
Copyright © 2020-2023  润新知