5404. 【NOIP2017提高A组模拟10.10】Graph

Graph

Description

给定一张 n 个点m条边的无向图,每条边连接两个顶点,保证无重边自环,不保证连通。
每次选择两条有公共点的两边匹配,被匹配过的边不能被再次匹配(即每条边最多匹配一次),输出最多的匹配对数和一种可行的匹配方案。

Data Constraint

n <=105 m <=2105

Solution

这题乍一看很难,其实想到了就很简单。( To be or not to be )
对于每个联通块,先做一遍 dfs ,跑出一棵 dfs 树。
接下来,从深度大的节点做起,从下往上做。
对于每个点:
1’:若当前 与该点相连的边的数量为偶数,便让这些边两两匹配即可。
2’:若当前 与该点相连的边的数量为奇数,除了连向父亲的边以外,剩下的偶数条边两两匹配即可。
每一次匹配完的边要及时删除。

对于上述的匹配方案, significantly ,若整个联通块内的总边数为偶数,则会在根节点恰好匹配完;若整个联通块内的总边数为偶数,则会在根节点恰好剩下一条边。
故上述匹配方案是最优策略。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

#define fo(i,j,l) for(int i=j;i<=l;i++)
#define fd(i,j,l) for(int i=j;i>=l;i--)

using namespace std;
typedef long long ll;
const ll N=1e5+1e3,M=4*N;
int used[M],lb[M],fa[N],la[N],ne[M],d[M],a1[M],a2[M],a3[M];
int n,m,k,l,o,cnt,x,y;

void read(int &o)
{
    o=0; char ch=' ';
    for(;ch<'0'||ch>'9';)ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar())o=o*10+ch-48;

}

void llb (int a,int b)
{
    ne[++o]=la[a]; la[a]=o; lb[o]=b;
}

void addans(int a,int b,int c)
{
    a1[++cnt]=a; a2[cnt]=b; a3[cnt]=c;
}

void dg(int o,int u)
{
    int y=la[o],kt=0,ls=0;
    while(y){
        if(!fa[lb[y]])fa[lb[y]]=o,dg(lb[y],y);
        if(!used[y]&&lb[y]!=fa[o])
        {
            if(!kt)kt=1,ls=y;
            else {
                kt=0;
                addans(lb[ls],o,lb[y]);
                used[ls]=used[d[ls]]=1;
                used[y]=used[d[y]]=1;
            }
        }
        y=ne[y];
    }
    if(u&&kt==1){
        addans(lb[ls],o,lb[d[u]]);
        used[ls]=used[d[ls]]=1;
        used[u]=used[d[u]]=1;
    }
}

int main()
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    cin>>n>>m;
    fo(i,1,m){
        read(x);read(y);
        llb(x,y);llb(y,x);
        d[o]=o-1; d[o-1]=o;
    }
    cnt=0;
    fo(i,1,n)
    if(!fa[i])
    fa[i]=-1,dg(i,0);
    printf("%d\n",cnt);
    fo(i,1,cnt)printf("%d %d %d\n",a1[i],a2[i],a3[i]);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值