百度之星 初賽二魔怔(模擬+並查集)
阿新 • • 發佈:2021-08-01
link
題意:
給一張無向完全圖,每條邊有一個顏色,為黑色或者白色。你初始在點 s 上,你每次可以從當前的點經過一條邊走到另一個點上,並將走過的邊的顏色翻轉。你想要把圖中所有的邊全都變為黑色,要求最小化走過的邊的條數,求這個最小值,或者判斷無解。
分析:
其實就是考慮情況,我們先看這個性質,就是歐拉回路的性質,如果有大於兩個度為奇數的點一定是不能完成任務的。
然後我們就得出他最多有兩個度為奇數的點,要不就沒有度為奇數。
既然我們要將0轉成1,那麼我們可以把0提取出來,之後就會變成一塊一塊的。
然後我們知道他必須分聯通塊,然後我們按起點在不在聯通塊上分類:維護聯通塊就要用到並查集,連線聯通塊內部的邊就是總邊數,然後是連線聯通塊與聯通塊之間的邊是需要走兩次,因為它原本就是1,所以如果不出意外的話 :
- 在聯通塊上,步數是所有聯通塊邊數和加上(聯通塊數量-1)*2,為什麼-1那,因為我們只需要連線聯通塊之間即可。
- 不在聯通塊上,步數是所有聯通塊邊數和加上(聯通塊數量)*2,為什麼不需要-1那,因為我們除了需要連線聯通塊之間,還需要有一條邊(從起點連到聯通塊上)。
這是不出意外的情況:
那麼出意外的情況: - 在聯通塊上:如果起點的度為偶數並且有奇數度點,那麼我們就無法到達所有邊,因為既然有兩個奇數度點,那麼我們到其中一個後,就不能在走了,另一個奇數度點到不了了。所以是無解
- 不在聯通塊上:也跟上面一樣,如果有奇數度點也是不行的。原因更上面一致。如果起點連上奇數度點那麼就將連結的奇數度點變成了偶數點,那麼我們新增的那條邊沒辦法重新走一遍,而如果起點連上偶數度點那麼他還有兩個奇數點還是到不了。
ll n, m, k; ll a[maxn], b[maxn]; string str; ll f(ll x) { if(b[x] == x) return x; return b[x] = f(b[x]); } map < ll, ll > mp; void solve() { mp.clear(); scanf("%lld%lld", &n, &m); for(int i = 1; i <= n; i++) { a[i] = 0; b[i] = i; } for(ll i = 2; i <= n; i++) { cin >> str; str = " " + str; for(ll j = 1; j < i; j++) { if(str[j] == '0') { a[j]++; a[i]++; ll fx = f(i); ll fy = f(j); b[fx] = fy; } } } ll ans = 0; ll sum = 0; ll cnt = 0; ll flag = 0; for(ll i = 1; i <= n; i++) { if(a[i] % 2) { ans++; } sum += a[i]; if(a[i]) { ll s = f(i); if(mp[s] == 0) { flag++; mp[s] = 1; } } } if(sum == 0) { cout << 0 << endl; return ; } sum = sum / 2 + (flag - 1) * 2; ///總數 if(ans > 2) { puts("-1"); return ; } else { if(a[m]) ///在 { if(a[m] % 2 == 0) ///偶數 { if(ans) puts("-1"); else { cout << sum << endl; } } else //奇數 { cout << sum << endl; } } else //不在 { if(ans == 0) { cout << sum+2 << endl; } else { cout << -1 << endl; } } } }