POJ 3020 / POJ 3041 关于二分图最大匹配的一些变形

其实这两个题早就过了,本想着等看完KM算法一块发的,可是最近一直在忙考试和CF,迟迟没写出来,感觉再拖下去这两个就要忘了,所以还是先发出来吧。

POJ 3020 二分图的最小顶点覆盖 等于 最大匹配


定义:设K为G的一部分的点的集合,若图G中的每一条边至少有一个顶点在K中,则称K为G的一点顶点覆盖。

    若G中不存在满足|K'| < |K|的点覆盖K' 则称K为G的最小点集覆盖。


设G为二分图,M为G的最大匹配,S为最小顶点覆盖的点集。

因为匹配M中的边两两没有交点,所以覆盖这M条边至少需要M个点。

若G中仍然存在两端都不为匹配点的边,则存在一条增广路,与M为最大匹配相矛盾。

对于一对匹配点,选择其中一个加入S时,若存在一条边未被覆盖,则将该点删除,加入另一点。

若两种情况均存在一天未被覆盖的边则说明崔在一条增广路,与M为最大匹配相矛盾。

故肯定存在一种选择方案使得在M对匹配点中选择M个点,使得所有的边均被覆盖,又因为覆盖M条

匹配边至少需要M个点,所以|s| == M。


POJ 3041  最大独立点集 等于 点的个数减去最大匹配

定义:设K为G的一部分的点的集合,若K中任意两点均不相邻,则称K为G的一个独立点集。

    若G中不存在满足|K'| > |K|的独立点集K',则称K为G的最大独立点集。

在一个图中,独立集和顶点覆盖有互补的性质。

T是G的独立点集  <=>  G中每条边至少有一个顶点在V(G) - T 中 <=> V(G) - T是G的一个顶点覆盖

而在二分图中最小点集覆盖等于最大二分匹配,所以最大独立点集等于点的个数减去最大匹配。


上述证明完全是自己瞎作的,不当之处,望不吝赐教。

下面说一下这两个题的思路:

其实两个题难度差不许多,基本属于拿来测代码的。

POJ 3041 可以将给出的点看成边,X,Y轴上的整数坐标位置看成点,则就抽象出了二分图,剩下的问题就迎刃而解了。

POJ 3020  给出的点作为点,两个相邻的点之间加一条边,因为不存在奇环,所以必为二分图。


POJ 3020

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>

#pragma comment(linker, "/STACK:1024000000");
#define LL unsigned long long int

using namespace std;

struct N
{
    int v;
    N *next;
}*head[410];

N *creat()
{
    N *p = (N*)malloc(sizeof(N));
    p->next = NULL;
    return p;
};

int point[41][11];

char s[41][11];

int jx[] = {-1, 0, 1, 0};
int jy[] = { 0,-1, 0, 1};

int Matching_Point[410];
bool mark[410];

bool dfs(int s)
{
    for(N *p = head[s]->next; p != NULL; p = p->next)
    {
        if(mark[p->v] == false)
        {
            mark[p->v] = true;
            if(Matching_Point[p->v] == -1 || dfs(Matching_Point[p->v]))
            {
                Matching_Point[p->v] = s;
                Matching_Point[s] = p->v;
                return true;
            }
        }
    }
    return false;
}

int Cal_Maximal_Matching(int n)
{
    memset(Matching_Point,-1,sizeof(Matching_Point));

    int i,ans = 0;

    for(i = 1; i < n; ++i)
    {
        if(Matching_Point[i] == -1)
        {
            memset(mark,false,sizeof(mark));
            if(dfs(i))
            {
                ans++;
            }
        }
    }
    return ans;
}

int main()
{
    int T,n,m,i,j,k,top,x,y;
    N *p;

    for(i = 0; i < 401; ++i)
    {
        head[i] = creat();
    }

    scanf("%d",&T);

    while(T--)
    {
        scanf("%d %d",&n,&m);

        for(i = 0; i <= n*m; ++i)
        {
            head[i]->next = NULL;
        }

        for(i = 0; i < n; ++i)
        {
            scanf("%*c%s",s[i]);
        }

        top = 1;
        memset(point,-1,sizeof(point));

        for(i = 0; i < n; ++i)
        {
            for(j = 0; j < m; ++j)
            {
                if(s[i][j] == '*')
                {
                    point[i][j] = top++;
                }
            }
        }

        for(i = 0; i < n; ++i)
        {
            for(j = 0; j < m; ++j)
            {
                if(point[i][j] != -1)
                {
                    for(k = 0; k < 4; ++k)
                    {
                        x = i+jx[k];
                        y = j+jy[k];
                        if(0 <= x && x < n && 0 <= y && y < m && point[x][y] != -1)
                        {

                            p = creat();
                            p->v = point[x][y];
                            p->next = head[point[i][j]]->next;
                            head[point[i][j]]->next = p;
                        }
                    }
                }
            }
        }
        printf("%d\n",top-1-Cal_Maximal_Matching(top));
    }
    return 0 ;
}


POJ  3041

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>

#pragma comment(linker, "/STACK:1024000000");
#define LL long long int

using namespace std;

const int MAXN = 1010;

struct N
{
    int v;
    N *next;
}*head[MAXN];

N *creat()
{
    N *p = (N *)malloc(sizeof(N));
    p->next = NULL;
    return p;
}

bool mark[MAXN];

int color[MAXN];

int Match_Point[MAXN];

bool dfs(int t)
{
    for(N *p = head[t]->next; p != NULL; p = p->next)
    {
        if(mark[p->v] == false)
        {
            mark[p->v] = true;
            if(Match_Point[p->v] == -1 || dfs(Match_Point[p->v]))
            {
                Match_Point[p->v] = t;
                Match_Point[t] = p->v;
                return true;
            }
        }
    }
    return false;
}

bool bfs(int n)
{
    queue<int> q;
    int s,i;
    N *p;

    for(i = 1;i <= n; ++i)
    {
        if(color[i] == -1)
        {

            q.push(i);
            color[i] = 0;

            while(q.empty() == false)
            {
                s = q.front();
                q.pop();

                for(p = head[s]->next; p != NULL; p = p->next)
                {
                    if(color[p->v] == -1)
                    {
                        color[p->v] = (color[s] == 1 ? 0 : 1);
                        q.push(p->v);
                    }
                    else if(color[p->v] == color[s])
                    {
                        return true;
                    }
                }
            }
        }
    }

    return false;
}

int Cal_Maximal_Matching(int n)
{
    int i,ans = 0;

    memset(Match_Point,-1,sizeof(Match_Point));

    for(i = 1;i <= n; ++i)
    {
        if(Match_Point[i] == -1)
        {
            memset(mark,false,sizeof(mark));
            if(dfs(i))
                ans++;
        }
    }
    return ans;
}

int main()
{
    int i,n,m,u,v;
    N *p;

    for(i = 0;i <= MAXN*2; ++i)
    {
        head[i] = creat();
    }

    while(scanf("%d %d",&n,&m) != EOF)
    {
        for(i = 1;i <= n; ++i)
        {
            head[i]->next = NULL;
        }

        while(m--)
        {
            scanf("%d %d",&u,&v);
            v += n;
            p = creat();
            p->v = v;
            p->next = head[u]->next;
            head[u]->next = p;
            p = creat();
            p->v = u;
            p->next = head[v]->next;
            head[v]->next = p;
        }

        printf("%d\n",Cal_Maximal_Matching(n*2));

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值