男生和女生(思维建图,最大独立集问题)

男生和女生(思维建图,最大独立集问题)

问题描述
在大学二年级,一些人开始了同学之间罗曼蒂克关系的研究。有“罗曼蒂克”关系是针对男生和女生之间的关系而言的。研究的原因是找到满足以下条件的最大集:这个集中没有两个学生有“罗曼蒂克”关系。现要你编程确定这样一个集中学生的人数。

输入
有若干组测试数据,每组测试数据表示一组研究对象,描述如下:
学生人数
对每个学生的描述,遵循如下格式:
学号:(罗曼蒂克关系的数目) 学号1 学号2…
或者是
学号:(0)
对n个对象的数据,学号是从0到n-1之间的整数,(n≤500)。

输出
对每组测试数据,输出一行,即没有“罗曼蒂克”关系的最大集中学生的人数。
在这里插入图片描述
将有“罗曼蒂克”的男生与女生之间连一条边。得到男生与女生关系二分图。
以样例一为例:
在这里插入图片描述
最大独立集的元素个数问题
在一个图G中的点集D,如果D中任意两点不相连,那么D称为G的一个独立集。而G中点数最多的独立集就是最大独立集。
二分图的最大独立集问题:
最大独立集点数 = 二分图G的顶点总数 – G的最大匹配数

本问题可转化为求G的最大独立集的元素个数问题。
问题再次转化为二分图的最大匹配问题,可采用的匈牙利算法。

本题中并没有告诉哪些是男生,哪些是女生。如何分离?
方法:依据有“罗曼蒂克”关系的两人必是一男一女来确定他们属于哪一个集合。即如确定一人是某种性别的,如男性,那么与之有关系是女性。
为方便处理,用取值0、1的数组is分别记载这两种性别的学生,分别称为0-集和1-集。
现在,开始时设所有学生都属于0-集,逐个考察每个学生的归类。从0-集中取未做考察标记的学生,从该学生开始考察。设当前考察的学生是i,那么凡是与i有关系的学生被归入(1-is[i])-集,同时对i做好考察标记。

分离算法实现过程:

		for(int i = 0; i < n; ++i){
            for(int j = 0; j < n; ++j){
                if(g[i][j]){
                    is[j] = 1 - is[i];
                }
            }
     	}

细节注意:
在已成功分离出0-组与1-组这两个组的数组is中,仍可能能还存在无法分清男女生的学生情况。
这些学生都被编入0-组;
他或她与其他学生之间都没有关系。
但这些学生的存在对本问题求最大独立集与最大匹配是没有影响的。

AC code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 500 + 50;
int link[maxn];
vector<vector<int> >G(maxn);
bool vis[maxn];
bool dfs(int u){
    for(int i = 0; i < G[u].size(); ++i){
        int v = G[u][i];
        if(!vis[v]){
            vis[v] = 1;
            if(link[v] == -1 || dfs(link[v])){
                link[v] = u;
                return true;
            }
        }
    }
    return false;
}

int hungary(int n){
    memset(link,-1,sizeof(link));
    int res = 0;
    for(int i = 0; i < n; ++i){
        memset(vis,0,sizeof(vis));
        if(dfs(i)) ++res;
    }
    return res;
}

int g[maxn][maxn];
int is[maxn];
int main(){
    int n;
    while(cin >> n){
        for(int i = 0; i <= n; ++i){
            for(int j = 0; j <= n; ++j){
                g[i][j] = 0;
            }
        }
        int x,y,cnt;
        for(int i = 0; i < n; ++i){
            scanf("%d: (%d)",&x,&cnt);
            for(int j = 0; j < cnt; ++j){
                scanf("%d",&y);
                g[x][y] = 1;
            }
        }
        if(n == 1){
            puts("1");
            continue ;
        }
        for(int i = 0; i <= n; ++i) is[i] = 0,G[i].clear();
        for(int i = 0; i < n; ++i){
            for(int j = 0; j < n; ++j){
                if(g[i][j]){
                    is[j] = 1 - is[i];
                }
            }
        }
        for(int i = 0; i < n; ++i){
            for(int j = i + 1; j < n; ++j){
                if(g[i][j] && is[i] != is[j]){
                    G[i].push_back(j);
                }
            }
        }
        printf("%d\n",n - hungary(n));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值