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

Description

给定一张n个点m条边的无向图,每条边连接两个顶点,保证无重边自环,不保证连通
你想在这张图上进行若干次旅游,每次旅游可以任选一个点x作为起点,再走到一个与x 直接有边相连的点y,再走到一个与y 直接有边相连的点z 并结束本次旅游
作为一个旅游爱好者,你不希望经过任意一条边超过一次,注意一条边不能即正向走一次又反向走一次,注意点可以经过多次,在满足此条件下,你希望进行尽可能多次的旅游,请计算出最多能进行的旅游次数并输出任意一种方案

Input

第1 行两个正整数n 与m,表示全图的点数与边数
下接m 行,每行两个数字u 与v 表示一条边

Output

第1 行一个整数cnt 表示答案
下接cnt 行,每行三个数字x, y 与z,表示一次旅游的路线
如有多种旅行方案,任意输出一种即可

Sample Input

4 5
1 2
3 2
2 4
3 4
4 1

Sample Output

1
2

Data Constraint

对于前20% 的数据,n <= 10;m <= 20.
对于令20% 的数据,m = n - 1,并且图连通
对于令10% 的数据,每个点的度数不超过2
对于100% 的数据,n <= 100000;m <= 200000

Solution

  • 由于这个图不一定是连通图,且两两联通块之间互不影响,所以这里只讨论一个联通快。

  • 对于一个边数为 m 联通快,显然能有 m2 条旅游路线,因为点能重复走。

-问题是如何连边呢?

  • 把连通块看成一棵树,遍历一遍,记录遍历的顺序。(注意只走尚未走过的点)

  • 按照遍历的顺序的倒序操作,对于一个点,又开一个队列。

  • 先把连“儿子”节点的边加入进去,有的话再将连“父亲”节点的边加入进去。

  • 这样两条两条进行组合,就能不重不漏地匹配,

  • 因为先匹配深度大、再匹配深度小的,就能保证深度大的一定能被匹配,深度小的可以以后再说。

  • 最后将每个连通块中匹配的边都一起输出即可。

Code

#include<cstdio>
using namespace std;
const int N=100001;
int tot,ans;
int first[N],next[N<<2],en[N<<2];
int fa[N],q[N],num[N],vis[N<<2],ans1[N][3];
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline void insert(int x,int y)
{
    next[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
}
inline void bfs(int x)
{
    int l=0,r=1;
    fa[q[1]=x]=-1;
    while(l<r)
        for(int i=first[q[++l]];i;i=next[i])
            if(!fa[en[i]]) fa[q[++r]=en[i]]=q[l];
    for(int i=r;i;i--)
    {
        num[0]=0;
        for(int j=first[q[i]];j;j=next[j])
            if(!vis[j] && en[j]!=fa[q[i]]) num[++num[0]]=j;
        for(int j=first[q[i]];j;j=next[j])
            if(!vis[j] && en[j]==fa[q[i]])
            {
                num[++num[0]]=j;
                break;
            }
        for(int j=1;j<num[0];j+=2)
        {
            vis[num[j]]=vis[num[j]^1]=vis[num[j+1]]=vis[num[j+1]^1]=true;
            ans1[++ans][0]=en[num[j]];
            ans1[ans][1]=q[i];
            ans1[ans][2]=en[num[j+1]];
        }
    }
}
int main()
{
    int n=read(),m=read();
    for(int i=tot=1;i<=m;i++)
    {
        int x=read(),y=read();
        insert(x,y);
        insert(y,x);
    }
    for(int i=1;i<=n;i++)
        if(!fa[i]) bfs(i);
    printf("%d\n",ans);
    for(int i=1;i<=ans;i++)
        printf("%d %d %d\n",ans1[i][0],ans1[i][1],ans1[i][2]);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值