Gym - 101670J(Punching Power)最大独立集+匈牙利

本文介绍了一种利用匈牙利算法求解最大独立集的方法,通过将点按x+y的奇偶性分为两组,确保每组内任意两点的距离大于1,从而求得满足条件的最大点集。

题意:

给出n个点的坐标,从中选几个点,要求任意两个点之间的距离不能小于1.3(因为坐标都是整数,两点之间距离为1或者1.414或者更大,则要求大于1即可)。

题解:

理论基础:最大独立集 = 点的个数(n)- 最大匹配,这里的最大匹配是指两点距离为1的最大匹配。
那么只要求出反向的最大匹配就ok了。
求最大匹配肯定要用到基于二分图的匈牙利算法,既然基于二分图,那么必须把这n个点分成两部分,如何分是个问题。
我们要求每一部分的任意两点距离都得大于1,这里有个小技巧【按照x+y的值,分成奇数和偶数两部分,若两个点x+y奇偶性相同,那么这两点距离一定是大于1的】

附代码: 

#include <set>
#include <map>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define clr(str,x) memset(str,x,sizeof(str))
#define FRER() freopen("in.txt","r",stdin);
#define FREW() freopen("out.txt","w",stdout);
#define INF 0x7fffffff
#define maxn 2010

typedef long long int ll;
using namespace std;
struct node
{
    int x;
    int y;
} nodes[maxn];
bool cmp(node a,node b)
{
    return a.x+a.y<b.x+b.y;
}
vector<int> edg[maxn];
bool vis[maxn];
int match[maxn];
bool Find(int x)
{
    for(int i=0; i<edg[x].size(); i++)
    {
        int j=edg[x][i];
        if(vis[j]==false)
        {
            vis[j]=true;
            if(match[j]==-1||Find(match[j]))
            {
                match[j]=x;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    //FRER()
    //FREW()
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1; i<=n; i++)
        {
            edg[i].clear();
            scanf("%d%d",&nodes[i].x,&nodes[i].y);
        }
        sort(nodes+1,nodes+1+n,cmp);
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                if((nodes[i].x-nodes[j].x)*(nodes[i].x-nodes[j].x)+(nodes[i].y-nodes[j].y)*(nodes[i].y-nodes[j].y)==1)
                {
                    edg[i].push_back(j);
                    edg[j].push_back(i);
                }
            }
        }
        memset(vis,false,sizeof(vis));
        memset(match,-1,sizeof(match));
        int num=0;
        for(int i=1; i<=n; i++)
        {
            if((nodes[i].x+nodes[i].y)&1)
            {
                memset(vis,false,sizeof(vis));
                if(Find(i))
                    num++;
            }
        }
        printf("%d\n",n-num);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值