1. 程式人生 > >Luogu P2661 信息傳遞

Luogu P2661 信息傳遞

www. 感謝 size 並不會 cst ref target blank printf

傳送門

一眼就能看出來是個並查集 但是並不會寫...

看了一下題解說是並查集求最小環qwq

所以,每次加入第i個小同學,判斷如果他要告訴的小同學k最後會告訴他(也就是轉回來了),

就說明出現了一個環,這時更新一下最小環;

否則就記一下他要告訴的小同學fa[x](為下一個環做鋪墊)

  (如果已經找到環就不記,否則下次有小同學指向這個環的時候就會進入死循環(感謝題解))

因為每個點的出度都是1,所以當某幾個點已經形成了一個環的時候,他們就不可能屬於別的環了,

也就是說每個點只能屬於0(入度 = 0)或1個環,

可以得出當前這個點形成的環一定是它所能形成的最優解(最短的)!

當發現環的時候,在getfa裏面用一個depth記錄深度(環的長度);

至於註意事項...一開始我寫的是

int getfa(int x,int &d) {
    d++;
    if(fa[x] == x)
        return x;
    return fa[x] = getfa(fa[x],d);
}

然而wa了,對照題發現最後一句是錯的qaq

於是我通過自己寫了一堆測試數據才想明白:

因為每次判斷都要調用一次getfa函數,所以即使沒有找到環,經過的小同學的fa[]也是動態變化的,

這就導致當最後找到環的時候,中間的很多步驟都被跳過了,

並且由於depth每次清零,所以最後得到的depth並不是真正的答案。

解決方案:不改變fa[x]

  return getfa(fa[x],d);

(其實我感覺把每個點的步數記下來也可以qwq但是沒寫出來...以後再說吧x)

完整代碼:

技術分享圖片
#include<cstdio>
#define min(x,y) (x)<(y)?(x):(y)
using namespace std;
int fa[1000005],n,k,dpth,ans = 10000005;
int getfa(int x,int &d) {
    d++;
    if(fa[x] == x)
        return x;
    return getfa(fa[x],d);
}
int main() {
    scanf(
"%d",&n); for(int i = 1; i <= n; i++) fa[i] = i; for(int i = 1; i <= n; i++) { scanf("%d",&k); dpth = 0; if(getfa(k,dpth) == i) ans = min(ans,dpth); else fa[i] = k; } printf("%d",ans); return 0; }
View Code

  

Luogu P2661 信息傳遞