uva12083(二分图最大独立集)

https://vjudge.net/problem/UVA-12083(uva12083 点击打开链接)

思路:二分图的最大独立集(二分图中任选两点之间,都不会有边相连)。

分析:首先,二分图肯定不能有边将同一侧的点相连。他们只要满足四个条件中的一个就不会产生爱意,相反,如果四个条件都不满足,就会产生爱意。每个人要么男,要么女,这样同侧就肯定不会有边相连,然后左右两边建边。如果其他三个条件都不满足,就建一条无向边。所以这个问题就变成了选择尽量多的点,满足任意一条边的两个点都不能同时被选到。这就是最大独立集。

那么怎么解这个最大独立集?其实它是和最小点覆盖互补的。互补就是指,数目是总点数减最小覆盖,解集只要把最小覆盖里的“已选点”和“未选点”互换即可。
为什么是互补?看两者的定义是什么。
(1)最小覆盖集:对于每条边,至少有一个点被选中。
(2)最大独立集:对于每条边,至少有一个点不被选中。
每个最小覆盖集都和一个唯一的最大独立集互补,每个最大独立集也都和一个唯一的最小覆盖集互补。
所以,综上:
最大独立集:选择尽量多的点,满足任意一条边的两个端点不被同时选中。
解集:与最小覆盖集互补。最大独立集点数 = 总点数 - 最小覆盖集点数 = 总点数 - 最大匹配数。

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>

using namespace std;

const int maxn = 500 + 10;
struct node
{
    int height;
    char sex;
    string music;
    string sport;
}person[maxn];

int t;
int line[maxn];
bool used[maxn];

bool judge(const node a,const node b)
{
    if(abs(a.height - b.height) <= 40 && a.sex != b.sex && a.music == b.music && a.sport != b.sport)
            return true;
        return false;
}

//板子:
bool dfs(int x)
{
//右边的点
    for(int i = 1;i <= t;i++)
    {
        if(i != x && used[i] == false)
        {
            if(judge(person[i],person[x]))
            {
                used[i] = 1;
                if(line[i] == 0 || dfs(line[i]))
                {
                    line[i] = x;
                    return true;
                }
            }
        }
    }
    return false;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&t);
        for(int i = 1;i <= t;i++)
        {
            cin >> person[i].height >> person[i].sex;
            cin >> person[i].music >> person[i].sport;
        }

        memset(line,0,sizeof(line));
        int ans = 0;
        for(int i = 1;i <= t;i++)//左边的点
        {
            memset(used,0,sizeof(used));
            if(dfs(i))
                ans++;
        }

        printf("%d\n",t - ans/2);
    }
}

模板:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=1001;
int n1,n2,k;
//n1,n2为二分图的顶点集,其中x∈n1,y∈n2
int map[N][N],vis[N],link[N];
//link记录n2中的点y在n1中所匹配的x点的编号
int find(int x)
{
    int i;
    for(i=1;i<=n2;i++)
    {
        if(map[x][i]&&!vis[i])//x->i有边,且节点i未被搜索
        {
            vis[i]=1;//标记节点已被搜索
            //如果i不属于前一个匹配M或被i匹配到的节点可以寻找到增广路
            if(link[i]==0||find(link[i]))
            {
                link[i]=x;//更新
                return 1;//匹配成功
            }
        }        
    }
    return 0;
}
int main()
{
    int i,x,y,s=0;
    scanf("%d%d%d",&n1,&n2,&k);
    for(i=0;i<k;i++)
    {
        scanf("%d%d",&x,&y);
        map[x][y]=1;
    }
    for(i=1;i<=n1;i++)
    {
        memset(vis,0,sizeof(vis));
        if(find(i))
            s++;
    }
    printf("%d\n",s);
    return 0;
}
  • 总算明白了点儿,明天再看看几者的关系吧,困了。。。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值