HDU 1068 Girls and Boys

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1068

题目大意:有人开展了在学生之间浪漫关系的研究。给定学生之间的关系,求两两之间没有浪漫关系的最大人数集合。

很显然,求的是最大独立集,独立集的意思就是说,集合里任意两个顶点都不会属于同一条边。

最大独立集=顶点数-最大匹配,而最大匹配可以转化成最大流或者是用匈牙利算法求出。

对第一组测试样例的解释。

由图可以看出,这7个点的最大匹配数是4,但是,有重复的,如上图,很显然0和4的匹配出现了两次,1和6的匹配出现了两次,故有效的匹配其实等于算出来的最大匹配除以2,所以,这里实际最大匹配数为2,由于最大独立集=顶点数-最大匹配数,即答案为5。

网上很多少其实看了别人题解都没看明白就照着代码敲了一遍,然后自己写博客就开始瞎BB了。说什么必须女配男。。。

下面是基于匈牙利算法的AC代码:

#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;
const int maxn=1005;
vector<int>G[maxn];
int vis[maxn],girl[maxn],n;
bool Find(int x)
{
    for(int i=0;i<G[x].size();i++)
    {
        int next=G[x][i];
        if(!vis[next])
        {
            vis[next]=1;
            if(!girl[next]||Find(girl[next]))
            {
                girl[next]=x;
                return true;
            }
        }
    }
    return false;
}
int solve()
{
    int ans=0;
    for(int i=0;i<n;i++)
    {
        fill(vis,vis+n+1,0);
        if(Find(i)) ans++;
    }
    return n-ans/2;
}
int main()
{
    while(~scanf("%d",&n))
    {
        int from,cnt,to;
        for(int i=0;i<=n;i++) G[i].clear();
        fill(girl,girl+n+1,0);
        for(int i=0;i<n;i++)
        {
            scanf("%d: (%d) ",&from,&cnt);
            while(cnt--)
            {
                scanf("%d",&to);
                G[from].push_back(to);
            }
        }
        printf("%d\n",solve());
    }
    return 0;
}

当然,转化成网络流也可以搞的。不过没有匈牙利算法那么快

下面是基于网络流的AC代码;

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int maxn=5005,inf=1<<30;
struct edge{int to,cap,rev;};
vector<edge>G[maxn];
int vis[maxn],n,m,k;
void add_edge(int from,int to,int cap)
{
    G[from].push_back((edge){to,cap,G[to].size()});
    G[to].push_back((edge){from,0,G[from].size()-1});
}
int dfs(int v,int t,int f)
{
    if(v==t) return f;
    vis[v]=true;
    for(int i=0;i<G[v].size();i++)
    {
        edge &e=G[v][i];
        if(!vis[e.to]&&e.cap>0)
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t)
{
    int flow=0;
    while(true)
    {
        fill(vis,vis+maxn,0);
        int f=dfs(s,t,inf);
        flow+=f;
        if(f==0) return flow;
    }
}
int main()
{
    //0~n-1:计算机对应的顶点
    //n~n+m-1:任务对应的顶点
    while(~scanf("%d",&n))
    {
        //scanf("%d%d",&n,&m);
        int s=n+n;//超级源点
        int t=s+1;//超级汇点
        for(int i=0;i<maxn;i++) G[i].clear();
        for(int i=0;i<n;i++) add_edge(s,i,1);//在源点跟计算机之间连边
        for(int i=0;i<n;i++) add_edge(n+i,t,1);//在汇点跟任务之间连边
        for(int i=0;i<n;i++)
        {
            int from,to,cap=1,cnt;
            scanf("%d: (%d) ",&from,&cnt);
            while(cnt--)
            {
                scanf("%d",&to);
                add_edge(from,n+to,cap);
            }
        }
        printf("%d\n",n-max_flow(s,t)/2);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值