Strategic game(无向?)二分图最小点覆盖(Poj1463,Uva1292)

原题链接
此题求二分图的最小点覆盖,数值上等于该二分图的最大匹配。得知此结论可以将图染色,建有向图,然后跑匈牙利/网络流,如下。然而...

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN=2000+5;
int q[MAXN],hd1[MAXN],hd2[MAXN];
int lnk[MAXN];
bool vis[MAXN],bw[MAXN];
int n,ft,rr,cnt1,cnt2;
struct Edge
{
    int t,n;
}e1[MAXN<<1],e2[MAXN<<1];

inline void build(int f,int t)
{
    e1[++cnt1]=(Edge){t,hd1[f]};
    hd1[f]=cnt1;
}

inline void build2(int f,int t)
{
    e2[++cnt2]=(Edge){t,hd2[f]};
    hd2[f]=cnt2;
}

void bfs()
{
    ft=rr=0;
    memset(bw,0,sizeof bw);
    memset(vis,0,sizeof vis);
    q[rr++]=0;
    vis[0]=1;
    bw[0]=1;
    while(ft<rr)
    {
        int u=q[ft++];
        for(int i=hd1[u];i;i=e1[i].n)
        {
            int v=e1[i].t;
            if(!vis[v])
            {
                vis[v]=1;
                bw[v]=bw[u]^1;
                q[rr++]=v;
            }
        }
    }
}

bool match(int u)
{
    for(int i=hd2[u];i;i=e2[i].n)
    {
        int v=e2[i].t;
        if(!vis[v])
        {
            vis[v]=1;
            if(lnk[v]==-1||match(lnk[v]))
            {
                lnk[v]=u;
                return 1;
            }
        }
    }
    return 0;
}

int main()
{
    while(~scanf("%d",&n))
    {
        cnt1=cnt2=0;
        memset(hd1,0,sizeof hd1);
        memset(e1,0,sizeof e1);
        memset(e2,0,sizeof e2);
        memset(hd2,0,sizeof hd2);
        memset(lnk,-1,sizeof lnk);
        
        int from,m,to;
        for(int i=0;i<n;++i)
        {
            scanf("%d:(%d)",&from,&m);
            for(int i=1;i<=m;++i)
                scanf("%d",&to),build(from,to),build(to,from);
        }
        bfs();
        for(int k=0;k<n;++k)
        {
            if(bw[k])
            {
                for(int i=hd1[k];i;i=e1[i].n)
                    build2(k,e1[i].t);
            }
        }
        int ans=0;
        for(int i=0;i<n;++i)
        {
            if(bw[i])
            {
                memset(vis,0,sizeof vis);
                if(match(i))
                    ++ans;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

然而我看网络上流传的都是另一种做法,直接输出在原无向图的最大匹配除以2,却很少有人证明(可能是各位大佬都认为这太显然了不用证)。仔细思考这个结论还是比较显然的(虽然我还想了一会),这里给出简单的证明,原来匹配一次的边被分别从从左右两个方向匹配了一次,这样每天匹配边就被记录了两次,又因为是求得的是最大匹配数,所以左右两边的匹配都应是最大匹配,故求给定无向图求最大匹配可以直接在原图求最大匹配,答案为该数值除以2

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN=2000+5;
int hd[MAXN],lnk[MAXN];
bool vis[MAXN];
int n,cnt;
struct Edge
{
    int t,n;
}e[MAXN<<1];

inline void build(int f,int t)
{
    e[++cnt]=(Edge){t,hd[f]};
    hd[f]=cnt;
}

bool match(int u)
{
    for(int i=hd[u];i;i=e[i].n)
    {
        int v=e[i].t;
        if(!vis[v])
        {
            vis[v]=1;
            if(lnk[v]==-1||match(lnk[v]))
            {
                lnk[v]=u;
                return 1;
            }
        }
    }
    return 0;
}

int main()
{
    while(~scanf("%d",&n))
    {
        cnt=0;
        memset(hd,0,sizeof hd);
        memset(e,0,sizeof e);
        memset(lnk,-1,sizeof lnk);
        int from,m,to;
        for(int i=0;i<n;++i)
        {
            scanf("%d:(%d)",&from,&m);
            for(int i=1;i<=m;++i)
                scanf("%d",&to),build(from,to),build(to,from);
        }
        int ans=0;
        for(int i=0;i<n;++i)
        {
            memset(vis,0,sizeof vis);
            if(match(i))
                ++ans;
        }
        printf("%d\n",ans>>1);
    }
    return 0;
}

还有DP解法,待填。
11.02UPD 树形DP解法

转载于:https://www.cnblogs.com/chwhc/p/7741655.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值