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\n", 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\n",u,x);
                }
            }
        }
    }
}

 

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

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

 

posted @ 2019-09-29 23:04  光芒万丈小太阳  阅读(258)  评论(0编辑  收藏  举报