圖的存在性

題目:

給定過一個1到N的排列P1到PN ,請判斷是否存在一個由N個點,N-1條邊組成的無向連通圖,滿足對於任意兩個整數i和j(i不等於j),若第i個點和第j個點之間有邊相連,則第Pi個點和第Pj個點之間同樣有邊相連。

輸入:
第一行輸入一個整數T表示T組數據,
每組數據格式爲:
第一行包括一個整數N,
第二行包含N個整數Pi到PN

輸出:
每組輸出一行,如果存在滿足條件的圖則輸出Yes,否則輸出No

樣例輸入:

2
4
4 3 2 1
3
3 1 2

樣例輸出:

Yes
No

解析:

將排列分解成多個環,那就存在以下幾種情況:

當最小環長度是1時,即至少有一個元素是在自己的位置,此時將這個點與其他n-1個點相連,就可以滿足要求;

當最小環長度是2時,只有在剩餘所有環的長度都是偶數時,才能滿足要求,這時對應的連接方式是最小環的兩個點之間有連線,其他環中,一半點和最小環中的第一個點相連,另一半點和最小環中另一個點相連;

當最小環的長度大於2時,這時無法滿足要求。

代碼:

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int N = 1e5+5;

int p[N];
bool vis[N];
vector<int> v;

int get_cycle_len(int cur, int cnt, int st){
    if(cur == st){
        return cnt + 1;
    }else{
        return get_cycle_len(p[cur], cnt+1, st);
    }
}
int main(){
    int t, n;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i = 1; i <= n; i++)
            cin>>p[i];
        memset(vis, 0, sizeof(vis));
        v.clear();
        for(int i = 1; i <= n; i++){
            if(!vis[i]){
                v.push_back(get_cycle_len(p[i], 0, i));
            }
        }
        sort(v.begin(), v.end());
        bool ans;
        if(v[0] == 1){
            ans = true;
        } else if(v[0] == 2){
            ans = true;
            for(int i = 1; i < v.size(); i++){
                if(v[i] % 2){
                    ans = false;
                    break;
                }
            }
        }else{
            ans = false;
        }
        if(ans) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章