NOIP 2015 提高組 Day1 信息傳遞

求最小的環的長度。 沒思路大概這已經卡死一小半人。

我的起初的想複雜了。 後來覺得:這道題真簡單:n個點,n-1條邊,如果沒有環的話,這是一棵樹。
這些題目應該不可能考察這些非常規的高難度的算法把。

求強聯分量

起初的想法,,然後在處理。
這些題目應該不可能考察這些非常規的高難度的算法把。
題外話:網上看了一個好東西:有人提出了這樣的算法。顯然該無向圖存在連通分支。對每個連通分支單獨進行考慮。

對於一個連通圖,任取一個它的生成樹(有算法可以完成這項操作),連通分支中除過這些樹枝剩下的邊我們稱作弦,每一條弦對應該連通圖的一個基本回路,無向圖的所有迴路都可以表示成這些基本回路的直和(邊集的並,去掉公共邊),所以最小回路一定在這些基本回路里產生。我們現在把這些弦一條一條往裏加就可以了。加入一條弦,立即產生一個基本回路,計算此迴路邊數並記錄在一個數組裏。去掉此弦,加入另一條弦,會有另一個迴路產生,計算邊數,保存。把所有的弦都試一遍,得到一個數組,取最小元素對應的弦,把弦加進生成樹,就得到最小回路。這樣的算法很節省空間。可以得到所有最小回路。
貌似合理:其實不行,看反例:
 -  fence1(1)
 / \ fence2(1),fence3(1)
 --- fence4(3)
/    \ fence5(1),fence6(1)
------fence7(6)
\____/fence 8(1),fence9(1),fence10(1)
可以發現,存在一種最小生成樹爲:

  -         fence1(1)
 /          fence2(1)
/    \      fence5(1),fence6(1)
\____/      fence8(1),fence9(1),fence10(1)

根據好友“川菜“思路:強聯通分量。
#include <iostream>
//#define debug
#include <bits/stdc++.h>
using namespace std;

const int MM=200001;
int  T[MM];
vector<int>  ReT[MM];
bool used[MM];
vector<int> xu;
int huan[MM];

void dfs(int v){
    used[v]=true;
    int next= T[v];
    if( next !=-1 && !used[next] )  {
            used[next]=true;
            dfs( next ) ;
    }
    xu.push_back(v);
}

void dfs2(int v,int k){
    huan[k]++;
    #ifdef debug
    cout <<"v :"<<v <<"  ||"<<"huan:"<<k << " "<< huan[k] <<"  ";
    #endif
    used[v]=true;
    for(int i=0;i<ReT[v].size();i++){
      int next=ReT[v][i];
      if( next !=-1 && !used[next] )  {
            used[next]=true;
            dfs2( next , k ) ;
      }
    }

}

int main()
{
    //freopen("message1.in","r",stdin);
    //freopen("mesaage.out","w",stdout);
    int N; cin>>N;
    memset(T,-1,sizeof(T));
    for(int i=1;i<=N;i++){
        cin>>T[i];
        //ReT[ T[i] ] = i;//這裏錯了。 反向以後,可能有好幾個兒子。
        ReT[ T[i] ].push_back(i);
    }

   #ifdef debug
    for(int i=1;i<=N;i++){
       cout << "i:" <<i <<"next:"<< T[i]<<";" ;
    }
    #endif // debug

    memset(used, 0 ,sizeof(used));
    for(int i=1;i<=N;i++){
        if( !used[i] ) {
             dfs(i);
        }
    }

    memset(used,0,sizeof(used));
    memset(huan,0,sizeof(huan));

    #ifdef debug
     for(int i=0;i<xu.size();i++){
            cout << "xu"<< i << ":" << xu[i]<<"  ";
            cout <<endl;
    }
    #endif // debug

     int k=0;
     for(int i=xu.size()-1;i>=0;i--){
        if( !used[ xu[i] ] ) {
             dfs2( xu[i], ++k );
        }
    }

    int ans=MM;
    for(int i=1;i<=k;i++){
       if( huan[i]!=1) ans = min( huan[i] , ans );
    }
    cout << ans;
    return 0;
}


思路二 拓撲 + DFS

  先拓撲,刪掉入度爲零的點,剩下的就是環了。 然後DFS
記下每次訪問到的節點的時間戳,如果再次訪問到該節點,將此時的時間戳,減去上次的時間戳。這應該是正解。
有環的話,只有一個環。 只有一個環嗎? 我想錯了!!
先掃描一遍結點,將入度爲0的結點入隊; 然後從隊列裏依次取出這些結點,刪除它的出邊,刪除該結點(做刪除標記),修改出邊結點的入度,如果其入度爲0,則入隊。 重複上述過程直至隊列爲空,拓撲過程結束。 剩下的結點一定構成環。
、在掃描一遍結點,遇到入度不爲0的結點,就以它爲 起點 dfs,查環的長度
 
感覺思路對的,luogu上值通過了10組數據。  寫後感: 剛開始 拓撲排序不熟練,通過了6組數據, 還有四組內存超過了。
#include <iostream>

#include <bits/stdc++.h>
using namespace std;

const int MM=200001;
int  T[MM];
int  rudu[MM];
int S;
int ans=MM;
int N;
bool used[MM];

//要優化,否則很多數據通不過

/* 1、先掃描一遍結點,將入度爲0的結點入隊;
 * 然後從隊列裏依次取出這些結點,刪除它的出邊,刪除該結點(做刪除標記),修改出邊結點的入度,如果其入度爲0,則入隊。
 * 重複上述過程直至隊列爲空,拓撲過程結束。 剩下的結點一定構成環。
 *
 * 2、在掃描一遍結點,遇到入度不爲0的結點,就以它爲起點查環的長度,同時刪除該結點(做刪除標記)。
 *
 */

void tp(){
    queue<int> q;
    for(int v=0; v<=N; v++){
        if( rudu[v]==0 ) {
                  q.push(v);
        }
    }

    while( !q.empty()){
        int f=q.front() ;  q.pop();
        int next = T[f];
        T[f]=-1;
        if(next!=-1)
        {   rudu[next]--;
            if( rudu[next]==0 )  {q.push( next ); }
        }
    }
}

void dfs(int v,int step){
    int next= T[v];
    if( next == S) {
        ans = min( ans, step ) ;
        return;
    }
    if( next !=-1 )  {
            dfs( T[v] , step+1) ;
            used[next]=true;
    }
}

int main()
{
    //freopen("message1.in","r",stdin);
   // freopen("mesaage.out","w",stdout);
     cin>>N;
    memset(rudu,0,sizeof(rudu));
    memset(used, 0 ,sizeof(used));

    for(int i=1;i<=N;i++){
        cin>>T[i];
        rudu[ T[i] ]++;
    }

    tp();
     //這裏可以不用深度搜索,直接while語句往下搜索,遇到起點後,統計一下環的長度,也方便
    memset(used, 0 ,sizeof(used));
    for(int i=1;i<=N;i++){
        if( rudu[i]==1 && !used[i] ) {
                S = i;
                used[i]=true;
                dfs(i, 1);
        }
    }

    cout << ans;
    return 0;
}


附: 數據2: 

輸入:

50 18 14 38 26 36 27 23 13 21 4 34 41 22 50 47 11 12 11 24 1 47 37 28 48 28 17 396 4 10 48 42 8 2 50 49 32 36 21 20 23 45 5 30 46 19 44 3 20 33

輸出:

15

樣例解釋:

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