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;
}
}
}
}