1. 程式人生 > >【2018-11-5模擬賽】T5 傳送機

【2018-11-5模擬賽】T5 傳送機

5、傳送機(sent.*)

問題描述:

黃黃同學要到清華大學上學去了。黃黃同學很喜歡清華大學的校園,每次去上課時總喜歡把校園裡面的每條路都走一遍,當然,黃黃同學想每條路也只走一遍。
我們一般人很可能對一些地圖是辦不到每條路走一遍且僅走一遍的,但是黃黃同學有個傳送機,他可以任意地將一個人從一個路口傳送到任意一個路口。
可是,每傳送一次是需要耗費巨大的內力的,黃黃同學希望可以用最少的傳送次數完成遊遍校園,你幫助他嗎?因為黃黃同學只是遊歷校園,於是我們可以認為黃黃同學可以從任意點開始,到任意點結束。
注意:不必經過所有的點。輸入格式:輸入第一行一個整數N,表示黃黃的校園裡一共有多少路口。第二行一個整數M,表示路口之間有M條路。後面M行,每行兩個整數a、b,表示a與b之間有一條路,且路是雙向的。輸出格式:輸出一行一個整數S,表示黃黃同學最少的傳送次數。

輸入樣例:

3
2
1 2
2 3

輸出樣例:

0

資料規模:

對於100%的資料滿足:N<=100000,M<=500000,1<=a,b<=N。


 

一句話概括,就是新增最少的邊,是他構成一個歐拉回路,也就是廣為人知的一筆畫問題。

要滿足一筆畫,我們要先使它構成一個連通圖且奇點個數不超過2(當然,那些只有一個點的連通塊不用算,因為既然它們沒有相連的邊,刪去也無所謂,用不著經過它們)

所以要加上max(連通快個數 - 1, 0)(由於可能所有連通塊都只有一個節點,一條邊也沒有)

也就是把所有的連通塊連起來,這樣構成了一個連通圖,然後還要考慮奇點的問題。

我們先考慮將連通塊全部連起來對奇點的影響。我們可以這樣考慮,對於沒有奇點的連通塊,直接將新增的兩條邊(連向其他塊)都連到一個點,這樣不會增加奇點個數。對於有2個及以上奇點的連通塊,將新增的邊連到任意兩個奇點上,這樣就減少了兩個奇點。

對於兩邊的塊,我們可以將有奇點的儘量放最邊上,如果沒有奇點的塊,增加兩個奇點是也就只有2個奇點,不會有影響;而對於有奇點的塊,我們也可以認為沒有影響,具體解釋比較麻煩,大家可以自行畫圖論證。

最後再把奇點消到只有2個即可(一條邊能消掉兩個奇點)

最後得出演算法。對於每個連通快 ans += (奇點個數 - 2) / 2 + 1

最後ans-=1(不解釋)

這裡我用並查集實現,大家看程式碼吧.

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005

int N, M;
int fa[MAXN];
int s[MAXN], a[MAXN]; int find( int x ){ if ( x == fa[x] ) return x; return fa[x] = find( fa[x] ); } void Merge( int x, int y ){ x = find(x); y = find(y); if ( x != y ) fa[x] = y; } int main(){ scanf( "%d%d", &N, &M ); if ( M == 0 ){ printf( "0\n" ); return 0; } for ( int i = 1; i <= N; ++i ) fa[i] = i; for ( int i = 1; i <= M; ++i ){ int x, y; scanf( "%d%d", &x, &y ); s[x]++; s[y]++; Merge( x, y ); } for ( int i = 1; i <= N; ++i ) a[find(i)] += s[i] % 2; int ans(0); for ( int i = 1; i <= N; ++i ) if ( s[i] > 0 && find(i) == i ) ans = ans + 1 + max( 0, ( a[i] - 2 ) / 2 ); printf( "%d\n", ans - 1 ); return 0; }

完結撒花 剛好下課