1. 程式人生 > >HDU 3018 Ant Trip (尤拉路)

HDU 3018 Ant Trip (尤拉路)

題目大意:一個隊想遍訪所有小城,每個隊每條路只能走一次,該隊可被分成了很多小隊,求得使能夠遍訪所有小城的最小隊數。

演算法:尤拉路+並查集

難度:NOIP

題解:

首先,用並查集處理出每個連通塊,並且讀入時統計每個點的度數,然後在每個連通塊內進行列舉,如果一個連通塊內都是偶數度的點,那麼這個連通塊就可以由一個小隊走完,如果這個連通塊內有奇數度的點,那麼就說明一個小隊是走不完的,該集合構成了非歐拉回路的其他連通集。則需要奇度總數/2個小隊數。因為一條尤拉路徑最多有兩個奇度點

注意:孤立的一個點不用去考慮!

程式碼如下(my code) 有點亂

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#define ll long long
#define N 100005
using namespace std;
int fa[N];
int findf(int x)
{
	if(x==fa[x]) return x;
	return fa[x]=findf(fa[x]);
}
int deg[N];
int vis[N];
struct node
{
	int idd;
	int tag;
}aa[N];
int cmp(node x,node y)
{
	return x.tag< y.tag;
}
int main()
{
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		memset(deg,0,sizeof(deg));
		memset(vis,0,sizeof(vis));
		memset(aa,0,sizeof(aa));//這個陣列一定要記得清!!! 
		for(int i = 1;i <= n;i++)
		{
			fa[i]=i;
		}
		for(int i = 1;i <= m;i++)
		{
			int a,b;
			scanf("%d%d",&a,&b);
			deg[a]++,deg[b]++;
			int t1=findf(a);
			int t2=findf(b);
			if(t1!=t2) fa[t1]=t2;
			vis[a]=1,vis[b]=1;
		}
		for(int i = 1;i <= n;i++)
		{
			if(!vis[i]) continue;
			int f=findf(i);
			aa[i].tag=f;
			aa[i].idd=i;
		}
		sort(aa+1,aa+1+n,cmp);
		int ans=0;
		int aas=0,ctt=0;
		for(int i = 1;i < n;i++)
		{
			if(aa[i].tag==0) continue;
			if(aa[i].tag==aa[i+1].tag)
			{
				aas+=deg[aa[i].idd]+deg[aa[i+1].idd];
				if(deg[aa[i].idd]%2) ctt++;
				if((deg[aa[i+1].idd]%2)&&(i==n-1)) ctt++;
			}else
			{
				if(ctt)
				{
					ans+=(ctt+1)/2;
				}else
				{
					ans+=1;
				}
				ctt=0;
				aas=0;
			}
			if(i==n-1)
			{
				if(ctt)
				{
					ans+=(ctt+1)/2;
				}else
				{
					ans+=1;
				}
				ctt=0;
				aas=0;
			}
		}
		printf("%d\n",ans);
	}
	return 0 ;
} 

大佬程式碼如下:


#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1e5+5;
int par[maxn],in[maxn],cnt[maxn],odd[maxn];
int N,M;
int a,b;
int ans;
 
void init()
{
    for(int i=1;i<=N;i++)
        par[i]=i;
    memset(in,0,sizeof(in));
    memset(cnt,0,sizeof(cnt));
    memset(odd,0,sizeof(odd));
    ans=0;
}
 
int find(int a)
{
    if(a==par[a])return a;
    return par[a]=find(par[a]);
}
 
void unite(int a,int b)
{
    a=find(a);
    b=find(b);
    if(a==b)return ;
    else if(a>b)
        par[a]=b;
    else
        par[b]=a;
}
 
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>N>>M)
    {
        init();
        for(int i=0;i<M;i++)
        {
            cin>>a>>b;
            unite(a,b);
            in[a]++;in[b]++;//統計節點度
        }
        for(int i=1;i<=N;i++)
        {
            cnt[find(i)]++;//統計i的根節點的節點數
            if(in[i]%2)odd[find(i)]++;//統計i的根節點的節點奇度數
        }
        for(int i=1;i<=N;i++)
        {
            if(cnt[i]<=1)continue;//獨立的點不用考慮或者不為根節點的點不考慮
            else if(odd[i]==0)ans++;//構成歐拉回路一筆畫
            else if(odd[i]>0)ans+=odd[i]/2;//沒有構成歐拉回路奇度/2
        }
        cout<<ans<<endl;
    }
    return 0;
}