1. 程式人生 > 實用技巧 >【Ant Trip】題解

【Ant Trip】題解

題目描述


給你無向圖的n個點和m條邊,保證這m條邊都不同且不會存在同一點的自環邊,現在問你至少要幾筆才能所有邊都畫一遍。(一筆畫的時候筆不離開紙)


輸入格式
多組資料,每組資料用空行隔開。
對於每組資料,第一行兩個整數n, m表示點數和邊數。接下去m行每行兩個整數a, b,表示a, b之間有一條邊。


輸出格式
對於每組資料,輸出答案。


樣例輸入
3 3
1 2
2 3
1 3
4 2
1 2
3 4


樣例輸出


1
2


分析

做這道題,首先我們需要知道這些:

  • 若一張圖只有一個點,那麼一筆都不需要畫
  • 假如他是一個半尤拉圖,那麼也只需要一筆
  • 以上兩種都不是的話,那麼我們需要畫的筆數應該等於這張圖中度為奇數的點數之和除以 2

那麼我們怎麼判斷是否為一個尤拉圖呢?暴搜?記得雷老師說過,假如一張圖是尤拉圖,那麼他的所有點的度應該都是偶數。
其實這道題的題目並沒有說所給出的資料是一張連通圖,所以我們又可以用一個並查集來求出每一個聯通分量,並用一個數組來儲存這個聯通分量之中的度為奇數的點的個數(好像有點囉嗦)


看懂了的話,可以自己實現一遍,發現有問題再看程式碼吧~

程式碼

#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 1e5 * 2 + 5;

int fa[MAXN];
int in[MAXN];
int num[MAXN];
int ans[MAXN];

int Find (int x) {
	if (fa[x] != x) fa[x] = Find (fa[x]);
	return fa[x];
}//找爸爸

int main() {
	int n, m;
	while ((scanf("%d %d", &n, &m)) != EOF) {
		for (int i = 1; i <= n; i++) fa[i] = i;
		memset (num, 0, sizeof num);
		memset (ans, 0, sizeof ans);
		memset (in, 0, sizeof in);//每次都要初始化陣列(錯了好幾次,害)
		for (int i = 1; i <= m; i++) {
			int x, y;
			scanf("%d %d", &x, &y);
			in[x] ++;
			in[y] ++;//度累加
			x = Find (x);
			y = Find (y);
			if (x != y) fa[x] = y;
            //假如他們不在一個聯通分量之中,就把他們放進同一個
		}
		for (int i = 1; i <= n; i++) {
			int x = Find (i); // i 的祖先。因為我們在之前是做了一個並查集的,假如說他們在一個連通分量之中,那麼他們的祖先就應該是相同的
			num[x] ++;//這個聯通分量中的點數累加
			if (in[i] % 2 == 1) ans[x] ++;//因為我們已經知道 i 是這個聯通分量中的值了,所以在這裡用 ans 陣列來累加他所在的連通分量的度為奇數的點的個數
            //此處其實就是用祖先來作為下標方便儲存,其實也可以在上面輸入時合併 x 和 y 的時候就用一個 vector 陣列來儲存以 x 為祖先的一個集合,最後看那些集合是有數的,就做一遍下面的操作,請自己實現!
		} 
		int sum_ = 0;//用於累加答案
		for (int i = 1; i <= n; i++) {
			if (num[i] <= 1) continue;//假如只有 1 個點,就不用管(0 個更不用)
			if (ans[i] == 0) sum_ ++;//假如這個連通分量之中所有點的度都沒有奇數,說明有尤拉路,一筆畫成
			else sum_ += ans[i] / 2;//以上兩種都不是的話,那麼我們需要畫的筆數應該等於這張圖中度為奇數的點數之和除以 2
		} 
		printf("%d\n", sum_);//完美結束
	}
	return 0;
}