UOJ 171 [WC2016]挑战NPC

一般图匹配

建图很巧妙。将一个筐子拆成3个点都连球,并且这三个点也连成一个环,然后跑一般图最大匹配。我们看和球相连的匹配,一定是n,这样一来就会最大化筐子的三元环的匹配数。 然而筐子的环上能匹配就意味着装不超过一个球(有两个点没有匹配上)。

给带花树打了一点注释

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define N 305
#define M 105
#define P 800
#define add2(a,b) {add(a,b);add(b,a);} 
using namespace std;
map<int,int> pos;
struct edge{int next,to;}e[300000];
int ecnt=0, mat[P], q[P], pre[P], sta[P], fa[P], vis[P], timer, head, tail, last[P], id[M][3], pcnt;
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void add(int a, int b)
{
    e[++ecnt]=(edge){last[a],b};
    last[a]=ecnt;
}
int lca(int x, int y)//暴力找LCA 
{
    ++timer;
    for(x=find(x), y=find(y);;swap(x,y))
    {
        if(x)
        {
            if(vis[x]==timer)return x;
            vis[x]=timer;
            x=find(pre[mat[x]]);
        }
    }
}
void blossom(int root, int x, int y)
{
    for(int m ;find(x)!=root;y=m, x=pre[y])
    {
        pre[x]=y;//保证任何时候从花上找到一个未匹配点,都可以沿着匹配边的方向向根更新,此时pre是双向的(pre[x]=y,pre[y]=x) 
        m = mat[x];
        if(sta[m]==1)
        {
            q[tail++]=m;
            sta[m]=0;
        }
        if(find(x)==x)fa[x]=root;
        if(find(m)==m)fa[m]=root;
    }
}
bool match(int s)
{
    memset(sta,-1,sizeof(sta));
    memset(pre,0,sizeof(pre));//pre记录前一个点 
    for(int i = 1; i <= pcnt; i++)fa[i]=i;
    q[0]=s;
    sta[s]=0;//起点记为偶点 
    for(head=0, tail=1; head<tail; head++)
    {
        int x=q[head];
        for(int i = last[x]; i; i=e[i].next)
        {
            int y=e[i].to;
            if(sta[y]==-1)
            {
                sta[y]=1;
                pre[y]=x;
                if(!mat[y])
                {
                    for(int mm; y; y=mm, x=pre[y])
                    {
                        mm=mat[x];
                        mat[y]=x;
                        mat[x]=y;
                    }
                    return 1;
                }
                else
                {
                    q[tail++]=mat[y];
                    sta[mat[y]]=0;
                }
            }
            else if(sta[y]==0 && find(x)!=find(y))
            {
                int temp=lca(x,y);
                //缩花,两个方向都要缩 
                blossom(temp,x,y); 
                blossom(temp,y,x);
            }
        }
    }
    return 0;
}
void clr()
{
    ecnt=0; timer=0;
    memset(last,0,sizeof(last));
    memset(mat,0,sizeof(mat));
    memset(vis,0,sizeof(vis));
}
void init()
{
    pcnt=N;
    for(int i = 1; i < M; i++)
        for(int j = 0 ; j < 3; j++)
        {
            id[i][j]=++pcnt;
            pos[pcnt]=i;
        }
}
int main()
{
    int T;
    scanf("%d",&T);
    init();
    while(T--)
    {
        clr();
        int n, m, e, ans=0;
        scanf("%d%d%d",&n,&m,&e);
        for(int i = 1; i <= e; i++)
        {
            int a, b;
            scanf("%d%d",&a,&b);
            for(int j = 0; j < 3; j++)
                add2(a,id[b][j]);
        }
        for(int i = 1; i <= m; i++)
            for(int j = 0; j < 3; j++)
                add2(id[i][j],id[i][(j+1)%3]);
        for(int i = 1; i <= pcnt; i++)
            if(!mat[i] && last[i])
                match(i);

        for(int i = 1; i <= m; i++)
            if(mat[id[i][0]]>N || mat[id[i][1]]>N)
                ans++;
        printf("%d\n",ans);
        for(int i = 1; i <= n; i++)
            printf("%d ",pos[mat[i]]);
        puts("");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值