1. 程式人生 > 其它 >百度之星 初賽二魔怔(模擬+並查集)

百度之星 初賽二魔怔(模擬+並查集)

題意:

給一張無向完全圖,每條邊有一個顏色,為黑色或者白色。你初始在點 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;
            }
        }
    }
}