OJ 1899 二分图的最大独立集

OJ 1899. Kaze

题目描述
给定一个 n个点 m条无向边的二分图,请输出一个图上的最大独立集。
输入图保证是一个二分图:存在 f : V ↦ { 0 , 1 } f : V\mapsto \{0, 1\} f:V{0,1},使得 ∀ ( u , v ) ∈ E , f ( u ) ≠ f ( v ) \forall (u, v) \in E, f(u) \neq f(v) (u,v)E,f(u)=f(v)
一个点集 S是独立集,当且仅当 ∀ ( u , v ) ∈ E , u ∉ S ∨ v ∉ S \forall (u, v) \in E, u \notin S \lor v \notin S (u,v)E,u/Sv/S为真。
最大独立集指独立集中大小最大的。

Input

第一行输入n,m
接下来 m 行,第 i 行为用空格隔开的整数 u i u_i ui v i v_i vi,表示一条 u i u_i ui v i v_i vi的无向边。
1 ≤ n ≤ 1000 1≤n≤1000 1n1000, 0 ≤ m ≤ ⌊ n / 2 ⌋ ⋅ ⌈ n / 2 ⌉ 0≤m≤⌊n/2⌋⋅⌈n/2⌉ 0mn/2n/2.保证没有重边与自环。

Output

第一行输出 k k k ( 1 ≤ k ≤ n ) (1 \leq k \leq n) (1kn),表示最大独立集的大小。
接下来一行输出 k个空格隔开的不重复正整数,表示一个最大独立集里点的标号。
如果有多种方案,输出任意一种。

Sample Input

Sample 1

3 2
1 2
2 3

Sample 2

4 4
1 3
1 4
2 3
2 4

Sample Output

Sample 1

2
1 3

Sample 2

2
1 2

Solution

 最大独立集=顶点数-最大匹配数
 先用匈牙利算法求最大匹配,然后合理取点求最大独立集。
匈牙利算法详解:https://blog.csdn.net/qq_49688477/article/details/119777445

Code

#include<iostream>
#include<vector>
#include<queue>
using namespace std;

int n,m;
vector<bool> vis;
vector<vector<bool>> g;//邻接矩阵
vector<int> part;
vector<int> l;
vector<int> r;
vector<bool> ans;

bool hungarian(int i){
    for(int j=1;j<n+1;j++){
        if(g[i][j]&&!vis[j]){
            vis[j]=true;
            if(l[j]==0||hungarian(l[j])){
                l[j]=i;
                r[i]=j;
                return true;
            }
        }
    }
    return false;
}

int main()
{
    cin>>n>>m;
    part.assign(n+1,0);
    l.assign(n+1,0);
    r.assign(n+1,0);
    ans.assign(n+1,false);
    g.assign(n+1,vector<bool>(n+1));
    int u, v;
    for(int i=0;i<m;i++){
        cin>>u>>v;
        g[u][v]=true;
        g[v][u]=true;
    }
    for(int i=1;i<n+1;i++){
        if(part[i]==0){
            queue<int> q;
            part[i]=1;//二分图左边的点
            q.push(i);
            while(!q.empty()){
                int cur=q.front();
                q.pop();
                for(int j=1;j<n+1;j++){
                    if(g[cur][j]){
                        if(part[j]==0){
                            part[j]=part[cur]*(-1);
                            q.push(j);
                        }
                    }
                }
            }
        }
    }
    for(int i=1;i<n+1;i++){
        //cout<<part[i]<<endl;
        if(part[i]!=1){
            continue;
        }
        vis.assign(n+1,false);
        if(!hungarian(i)){
            ans[i]=true;
        }
    }
    for(int i=1;i<n+1;i++){
        if(part[i]==1){
            ans[i]=true;
        }
    }
    for(int i=1;i<n+1;i++){
        //cout<< i<<"i "<<l[i]<<" "<<r[i]<<endl;
        if(part[i]==-1&&l[i]==0){
            vis.assign(n+1,false);
            vis[i]=true;
            queue<int> ql;
            queue<int> qr;
            qr.push(i);
            ans[i]=true;
            while(!qr.empty()){
                int vexr=qr.front();
                qr.pop();
                for(int j=1;j<n+1;j++){
                    if(g[vexr][j]&&!vis[j]){
                        ql.push(j);
                        ans[j]=false;
                    }
                }
                while(!ql.empty()){
                    int vexl=ql.front();
                    ql.pop();
                    if(r[vexl]!=0 && !vis[r[vexl]]){
                        qr.push(r[vexl]);
                        vis[r[vexl]]=true;
                        ans[r[vexl]]=true;
                    }
                }
            }
        }
    }
    int count=0;
    for(int i=1;i<n+1;i++){
        if(ans[i]){
            count++;
        }
    }
    cout<<count<<endl;
    for(int i=1;i<n+1;i++){
        if(ans[i]){
            cout<<i<<" ";
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值