图论训练一B

B - Ant Trip
Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

Description

Ant Country consist of N towns.There are M roads connecting the towns. 

Ant Tony,together with his friends,wants to go through every part of the country. 

They intend to visit every road , and every road must be visited for exact one time.However,it may be a mission impossible for only one group of people.So they are trying to divide all the people into several groups,and each may start at different town.Now tony wants to know what is the least groups of ants that needs to form to achieve their goal. 
 

Input

Input contains multiple cases.Test cases are separated by several blank lines. Each test case starts with two integer N(1<=N<=100000),M(0<=M<=200000),indicating that there are N towns and M roads in Ant Country.Followed by M lines,each line contains two integers a,b,(1<=a,b<=N) indicating that there is a road connecting town a and town b.No two roads will be the same,and there is no road connecting the same town.
 

Output

For each test case ,output the least groups that needs to form to achieve their goal.
 

Sample Input

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

Sample Output

     
1 2

Hint

New ~~~ Notice: if there are no road connecting one town ,tony may forget about the town. In sample 1,tony and his friends just form one group,they can start at either town 1,2,or 3. In sample 2,tony and his friends must form two group.


题意:有n个小镇,m条路,每条路可以通向两个小镇,有可能有的小镇没有路通向,即不可到达,Tony想和他的朋友们走遍所有可以通达的小镇,但是每条路只能走一次,完成这样一个任务最少需要几个人?

转化成图的模型,该图有n个点,有孤立点,有子图,孤立点不需要人去走,忽略,然后每个子图,如果是一个欧拉回路的图,那么这个子图只需要一个人去走,如果这个子图不是欧拉图,可以将他划分成欧拉通路,一条欧拉通路只需要一个人如走,即求最少欧拉通路即可;;



欧拉回路与欧拉通路判断:

欧拉通路:有向图:图联通,有一个顶点出度比入度大1,一个点入度比出度大一,其余出度==入度;

无向图:图联通,只有两个顶点的度数是奇数,其余顶点度数都是偶数;

欧拉回路: 有向图:图联通,所有顶点入度==出度;

无向图:图联通,所有顶点度数都是偶数;



本题目中可以看出都是无向图,如果这个子图是欧拉回路的图,那么这个子图所有顶点度数为偶数;如果不是欧拉回路图,便去求最少欧拉通路,由欧拉通路判断条件可以这样求最少欧拉通路个数:欧拉通路个数=奇度顶点个数/2

 


分析完毕,可以做题了,先用并查集将图保存为k个集合的形式,用集合(set) 将所有出现的顶点保存,这样孤立顶点直接就忽略了,同时用一个数组记录每个点出现次数,该次数便是该点的度数,然后遍历所有集合(set)当中的点,将k个集合的树根用不定数组保存,并用一个数组dug将每个集合(并查集)里面奇度顶点个数保存下来,遍历不定数组,即遍历每个集合(并查集),集合 i 奇度顶点数为0的是欧拉回路,ans+1,大于0的,加上奇度顶点个数的一半,ans+dug[i]/2,下面是我的代码:



<pre name="code" class="cpp">#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>
using namespace std;
const int maxn=200020;
int par[maxn],rank1[maxn];
void init(int n)
{
    for(int i=0; i<=n; i++)
    {
        par[i]=i;
        rank1[i]=0;
    }
}
int find1(int x)
{
    if(par[x]==x)
        return x;
    else
    {
        return par[x]=find1(par[x]);
    }
}
void unite(int x,int y)
{
    x=find1(x);
    y=find1(y);
    if(x==y)
        return;
    if(rank1[x]<rank1[y])
    {
        par[x]=y;

    }
    else
    {
        par[y]=x;
        if(rank1[x]==rank1[y])
            rank1[x]++;
    }

}
set<int>s;
set<int>::iterator it;
vector<int>root;

int vis[maxn],dug[maxn],du[maxn];
int main()
{
    int u,v;
    int n,m;
    int ans;
    while(~scanf("%d%d",&n,&m))
    {
        init(n);
        s.clear();
        root.clear();
        memset(du,0,sizeof(du));
        for(int i=0; i<m; i++)
        {
            scanf("%d%d",&u,&v);
            unite(u,v);
            s.insert(u);///set
            s.insert(v);
            du[u]++;
            du[v]++;

        }
        ans=0;
        int temp;
        memset(dug,0,sizeof(dug));
        memset(vis,0,sizeof(vis));
        for(it=s.begin(); it!=s.end(); it++)
        {
            temp=find1(*it);///temp表示树根
            if(vis[temp]==0)
            {
                vis [temp]=1;
                root.push_back(temp);
            }
            if(du[*it]%2==1)
                dug[temp]++;
        }
        for(int i=0; i<root.size(); i++)
        {
            int now=root[i];

            if(dug[now]!=0)
                ans+=dug[now]/2;
            else
                ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}


 
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值