HDU - 3081 Marriage Match II(最大流+并查集+二分查找)

7 篇文章 0 订阅
7 篇文章 0 订阅

点击查看原题
题目大意:一个(很恶俗的)交友游戏,男女搭配,女生选男生,选择的规则是可以选自己认可的也可以选自己的朋友(朋友的朋友,朋友的朋友的朋友)认可的。每轮可以选一个,并且下轮不能选已经被自己选过的。问最多可以玩几轮。
分析(建图):
因为一个团体可以选团体内任何人认可的人匹配,所以先并查集把团体分类,然后让内部每个女生都可以选团体可以选的男生,流量为1。
因为玩的轮数最多为n所以二分答案(k)去建立 源点到女生,男生到汇点的边容量为k。
跑最大流 如果 最大流等于n*k那么成立,二分答案。得到最终结果
AC代码:

#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define inf 100000000
using namespace std;
int n,m,k;
//最大流 
struct tree
{
    int from,to,cap,flow;
    tree(){}
    tree(int ff,int tt,int cc,int ww)
    {
        from=ff,to=tt,cap=cc,flow=ww;
    }
};
vector<tree>G;
vector<int>v[300000];
int cur[300000];
int d[300000];
int que[3000000];
set<int> indirectfrd[505];
vector<int> directfrd[505];
void addtree(int from,int to,int cap)
{
    G.push_back(tree(from,to,cap,0));
    G.push_back(tree(to,from,0,0));
    int m=G.size();
    v[from].push_back(m-2);
    v[to].push_back(m-1);
}
int sink,source;
bool bfs()
{
    memset(d,-1,sizeof(d));
    int front=0,back=0;
    que[back++]=source;
    d[source]=0;
    while(front<back)
    {
        int p=que[front++];
        if(p==sink) return true;
        for(int i=0;i<v[p].size();i++)
        {
            tree &e=G[v[p][i]];
            if(d[e.to]==-1&&e.flow<e.cap)
            {
                 d[e.to]=d[p]+1;
                 que[back++]=e.to;
            }
        }
    }
    return false;
}
int dfs(int x,int a)
{
    if(x==sink||a==0)
    return a;
    int flow=0,f;
    for(int &i=cur[x];i<v[x].size();i++)
    {
        tree &e=G[v[x][i]];
        if(d[e.to]==d[x]+1&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0)
        {
            e.flow+=f;
            G[v[x][i]^1].flow-=f;
            flow+=f;
            a-=f;
            if(a==0)
            break;
        }
    }
    if(!flow) d[x]=-2;
    return flow;
}
int maxflow()
{
    int flow=0;
    while(bfs())
    {
        memset(cur,0,sizeof(cur));
        flow+=dfs(source,inf);
    }
    return flow;
}

//并查集 
int rankk[505];
int father[505];
void init()
{
    for(int i=0;i<=n;i++)
    {
        rankk[i]=0;
        father[i]=i;
    }
}
int find(int x)
{
    if(father[x]==x) return x;
    else return father[x]=find(father[x]);
}
bool unite(int a,int b)
{
    a=find(a);
    b=find(b);
    if(a==b) return false;
    if(rankk[a]<rankk[b]) father[a]=b;
    else
    {
        father[b]=a;
        if(rankk[a]==rankk[b])
        rankk[a]++;
    }
    return true; 
}
bool getans(int mid)
{
    for(int i=0;i<300000;i++)
    {
        v[i].clear();
    }
    G.clear();
    source=1;sink=3*n;
    for(int i=1;i<=n;i++)
    {
        addtree(source,i+1,mid);
        addtree(i+n+1,sink,mid);
        for(set<int>::iterator it=indirectfrd[i].begin();it!=indirectfrd[i].end();it++)
        {
            addtree((*it)+1,i+n+1,1);
        }
    }
    if(maxflow()==mid*n)
    return true;
    return false;

}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {

        scanf("%d%d%d",&n,&m,&k);
        init();
        for(int i=0;i<=n;i++)
        {
            indirectfrd[i].clear();
            directfrd[i].clear();
        }
        for(int i=0;i<m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            directfrd[a].push_back(b);
        }
        for(int i=0;i<k;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            unite(a,b);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(find(i)==find(j))
                {
                    //printf("!! %d %d %d %d\n",i,j,find(i),find(j));
                    for(int l=0;l<directfrd[j].size();l++)
                    {
                        indirectfrd[i].insert(directfrd[j][l]);
                    }
                }
            }
        }
        //二分查找
        int l=1,r=n;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(getans(mid))
            {
                l=mid+1;
            }
            else
                r=mid-1;
        } 
        printf("%d\n",l-1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值