(最大独立集+拆点)洛谷 P6268 [SHOI2002]舞会

13 篇文章 0 订阅
9 篇文章 0 订阅

这题就是要找最大独立集,即两两都没有跳过舞的最大集合,所以只要把每个人拆成两个点,如果ab两个人跳过舞,就给a,b'和a',b连边,最后ans=n-最大匹配/2,这最大匹配我跑的hk,快当然快一些,不过我想就匈牙利算法应该就可以了

 特意写一下:

因为没有指定性别,所以要拆点,把i拆分i和i’ 那么U=V-M (M是最大匹配,U最大独立集,V是顶点数) 

2U=2V-2M  所以 U=n-M'/2

 

题目描述

某学校要召开一个舞会。已知学校所有 nnn 名学生中,有些学生曾经互相跳过舞。当然跳过舞的学生一定是一个男生和一个女生。在这个舞会上,要求被邀请的学生中的任何一对男生和女生互相都不能跳过舞。求这个舞会最多能邀请多少个学生参加。

输入格式

输入的第一行是 nnn 和 mmm 。其中 nnn 是可选的学生的总数, mmm 是已知跳过舞的学生的对数 ( n≤1000n \leq 1000n≤1000 , m≤2000m \leq 2000m≤2000 )。

然后有 mmm 行,每行包括两个非负整数,表示这两个编号的学生曾经跳过舞。学生的编号从 000 号到 n−1n - 1n−1 号。

输出格式

输出文件仅一行,为一个数字,即能够邀请的最多的学生数。

输入输出样例

输入 #1

8 6
0 2
2 3
3 5
1 4
1 6
3 1

输出 #1

5

输入 #2

20 5
5 2
4 3
18 17
0 11
13 3

输出 #2

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
const int MAXN=1205;
struct point
{
    double x,y,s;
}a[MAXN],b[MAXN];
bool map[MAXN][MAXN];
bool vis[MAXN];
int dx[MAXN],dy[MAXN];/*dx[i]表示左集合i顶点的距离编号,dy[i]表示右集合i顶点的距离编号,还有就是这里就是bfs找增广路用的数组 对于u-->v可达就有dy[v] = dx[u] + 1 */
int cx[MAXN],cy[MAXN];
int dis;
int n,T,m;
bool searchpath()
{
    queue<int>q;
    dis=INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=1;i<=n;i++)
    {
        //cx[i]表示左集合i顶点所匹配的右集合的顶点序号
        if(cx[i]==-1)
        {
            //将未遍历的节点 入队 并初始化次节点距离为0
            q.push(i);
            dx[i]=0;
        }
    }
    //广度搜索增广路径
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        if(dx[u]>dis) break;
        //取右侧节点,所以用m。
        for(int v=1;v<=m;v++)
        {
            //右侧节点的增广路径的距离
            if(map[u][v]&&dy[v]==-1)
            {
                dy[v]=dx[u]+1;//v对应的距离 为u对应距离加1
                if(cy[v]==-1) dis=dy[v];
                else
                {
                    dx[cy[v]]=dy[v]+1;
                    q.push(cy[v]);
                }
            }
        }
    }
    return dis!=INF;
}
 //寻找路径 深度搜索
int dfs(int u)
{
    for(int v=1;v<=m;v++)
    {
        //如果该点没有被遍历过 并且距离为上一节点+1
        if(map[u][v]&&!vis[v]&&dy[v]==dx[u]+1)
        {
            //对该点染色,即是进行标记
            vis[v]=true;
            if(cy[v]!=-1&&dy[v]==dis)
            continue;
            if(cy[v]==-1||dfs(cy[v]))
            {
                cx[u]=v;
                cy[v]=u;
                return 1;
            }
        }
    }
    return 0;
}
 //得到最大匹配的数目
int MaxMatch()
{
    memset(cx,-1,sizeof(cx));
    memset(cy,-1,sizeof(cy));
    int res=0;
    while(searchpath())
    {
        memset(vis,false,sizeof(vis));
        for(int i=1;i<=n;i++)
        {
            if(cx[i]==-1)
            res+=dfs(i);
        }
    }
    return res;
}
int main()
{
    int t;
    while(scanf("%d %d",&n,&t)!=EOF)
    {
        m=n;
        memset(map,false,sizeof(map));
        /*for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                map[i][j]=0;
            }
        }*/
        for(int i=1;i<=t;i++)
        {
            int u,v;
            scanf("%d %d",&u,&v);
            u++;v++;
            map[u][v]=1;map[v][u]=1;
        }
        int ans=MaxMatch();
        printf("%d\n",n-ans/2);
    }
}

 

16
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值