信息傳遞

這裏給你們介紹一種奇怪的作法,爲何奇怪呢,由於這玩意兒又不像並查集,又不像拓撲序ios

實際上是我模擬賽的時候先想的拓撲序,又想的並查集,而後一步步改爲了如今這個樣子算法

思路

  1. 首先,咱們經過題目,發現這是一個有向非聯通圖,且每一個點有且僅有一條出邊數組

  2. 其次,答案只會存在於如下兩種狀況中spa

    QQ截圖20191111170929.png

    圖一code

    QQ截圖20191111170909.png

    圖二blog

\(3.\;\)通常咱們使用拓撲序時,是要從入度爲 \(0\) 的點開始遍歷,可是顯然下面這張圖不會被遍歷到,那若是從入度爲 \(1\) 的點開始呢?ci

QQ截圖20191112061134.png

\(4.\;\)那麼這張圖也顯然,只有入度爲 \(0\)\(2\) 的點,以此類推,單純從任何一種入度爲某個值的點開始遍歷是不合適的。string

\(5.\;\)因此咱們仍是考慮最上面兩張基本的圖,圖一帶着個「小尾巴」,必定有入度爲 \(0\) 的點;圖二自己是一個大環,只有入度爲 \(1\) 的點
\(6.\;\)若是一遍不行,爲何不遍歷兩遍呢?思路也就出來了。it

實現

關於實現,我在模擬賽時還發現了兩個小問題io

  1. 若是對每個點判斷,而後遍歷,時間複雜度比較高,可能會 \(TLE\),我在模擬的時候先打了這個複雜度較高的算法,時間複雜度大概是 \(O(n^2)\),這樣交上去是有風險的,寧願要一個常數大一點的 \(O(n)\) 算法,咱也不能 \(T\) 是否是?

$Sol: $ 因此咱們在兩次遍歷前,各預處理一下,處理掉沒用的點,以後在剩下的點裏面選起點遍歷就行了。

  1. 屢次訪問到同一個點致使答案被不正確地更新如何處理

\(Sol:\) 我用一個數組,記錄一個點是被哪次的 \(DFS\) 遍歷的,另外,因爲我懶,我沒有新開數組,而是用的入度 \(in\) 數組,又防止混淆,我用的負數,下面舉個栗子

QQ截圖20191112063012.png

第一步,從 \(1\) 開始遍歷, \(1,2,3,4,5,6\)\(in\) 數組均被更新成 \(-1\),第二次從 \(8\) 開始,\(8,7\) 被更新爲 \(-2\),這時遍歷到 \(2\),通過判斷,\(in[7]\not=in[2]\),不更新答案

Code

各變量表示:

\(cnt, tot\) 計數用

\(to\) 每一個點指向的節點

\(vis\) 判斷是否被遍歷過,順便記錄深度

\(in\) 初期是每一個點的入度,後期也變成了一個判斷數組

\(zero\) 記錄入度爲 \(0\) 的點

\(one\) 記錄入度爲 \(1\) 的點

代碼自認爲可讀性較高

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 200005;
int cnt, to[N], n, vis[N], ans = 1000000007, in[N], zero[N], one[N];

void dfs(int k, int tot)
{
    if(vis[to[k]] && in[to[k]] == tot)
        ans = min(ans, vis[k] + 1 - vis[to[k]]);
    else if(vis[to[k]] && in[to[k]] != tot)
        return;
    else {
        vis[to[k]] = vis[k] + 1;
        in[to[k]] = tot;
        dfs(to[k], tot);
    }
}

int main()
{
//  freopen("message.in", "r", stdin);
//  freopen("message.out", "w", stdout);
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> to[i];
        in[to[i]]++;
    }
    cnt = 0;
    for(int i = 1; i <= n; i++) {
        if(in[i] == 0 && vis[i] == 0) {
            zero[++cnt] = i;
        }
    }
    int tot = 0;
    for(int i = 1; i <= cnt; i++) {
        if(vis[zero[i]] == 0) {
            vis[zero[i]] = 1;
            dfs(zero[i], --tot);
        }
    }
    cnt = 0;
    for(int i = 1; i <= n; i++) {
        if(in[i] == 1 && vis[i] == 0) {
            one[++cnt] = i;
        }
    }
    for(int i = 1; i <= cnt; i++) {
        if(vis[one[i]] == 0) {
            vis[one[i]] = 1;
            in[one[i]] = 1;
            dfs(one[i], 1);
        }
    }
    cout << ans << "\n";
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}
相關文章
相關標籤/搜索