در گراف جهتدار به جفت راس \((v, u)\) قویا همبند گوییم اگر و تنها اگر مسیری از \(v\) به \(u\) و مسیری از \(u\) به \(v\) وجود داشته باشد.
به عنوان مثال در شکل 1، راسهای 2 و 4 یک جفت راس قویا همبند هستند.
گراف قویا همبند، گراف جهتداری است که هر جفت راس آن قویا همبند باشند (شکل 1).
یک مولفه قویا همبند در گراف جهتدار \(G\) ، زیرمجموعهای از رئوس \(G\) میباشند که در زیرگراف القایی آنها تشکیل یک گراف قویا همبند را دهد و همچنین ماکسیمال باشد، یعنی نتوان راس دیگری به آن اضافه کرد.
صورت لم: هر راس \(v\) در گراف جهتدار \(G\) ، تنها در یک مولفه قویا همبند یکتا قرار دارد.
اثبات: از برهان خلف استفاده میکنیم. فرض کنید که \(v\) در دو مولفه قویا همبند \(H\) و \(L\) قرار دارد و \(u\) راسی در \(L\) باشد که در \(H\) قرار ندارد. چون \(u\) و \(v\) در یک مولفه قویا همبند قرار دارند، در نتیجه از هر راسی در \(H\) میتوان با مسیری به \(v\) آمد و سپس با مسیری به \(u\) رفت و همچنین با مسیری از \(u\) به \(v\) آمد و با مسیری به هر راس دلخواه در \(H\) رفت. پس درنتیجه میتوان راس \(u\) را به مولفه \(H\) اضافه کرد که این با ماکسیمال بودن \(H\) در تناقض است. از تناقض حاصل درستی حکم نتیجه میشود.
میتوان هر گراف جهتدار \(G\) را به مولفههای قویا همبند افراز کرد. شکل 2 گراف جهتداری را نمایش میدهد که مولفههای قویا همبند آن مشخص شدهاند.
گراف معکوس \(G\) ، گرافی است که از معکوس کردن جهت یالهای گراف \(G\) بدست میآید. توجه کنید یک گراف قویا همبند است اگر و تنها اگر معکوس آن نیز قویا همبند باشد.
فرض کنید \(G\) ، یک گراف جهتدار دلخواه باشد و گراف جهتدار \(H\) گرافی باشد که هر راس آن متناظر با یک مولفه قویا همبند در \(G\) است و هر مولفه قویا همبند در \(G\) به دقیقا یک راس در \(H\) متناظر شود. اگر \(v\) راسی در \(H\) باشد، مولفه قویا همبند متناظر با راس \(v\) در \(G\) را با \(F(v)\) نمایش میدهیم. اگر \(v\) و \(u\) دو راس از \(H\) باشند، به ازای هر یال جهتدار از رئوس \(F(v)\) به رئوس \(F(u)\) ، یالی جهتدار از \(v\) به \(u\) قرار دارد و همچنین هر یالی که از \(v\) به \(u\) است، متناظر با یالی از رئوس \(F(v)\) به رئوس \(F(u)\) میباشد. در این صورت به \(H\) ، یک گراف فشرده شده مولفههای قویا همبند \(G\) میگوییم.
صورت قضیه: هر گراف فشرده شده مولفههای قویا همبند، دور ندارد.
اثبات: فرض کنید \(G\) یک گراف جهتدار دلخواه است و \(H\) ، گراف فشرده شده مولفههای قویا همبند \(G\) باشد.از برهان خلف استفاده میکنیم. فرض کنید \(H\) دور داشته باشد و دو راس مانند \(v\) و \(u\) از \(H\) در یک دور قرار داشته باشند. چون از هر راس در یک مولفه قویا همبند به هر راس آن مولفه مسیر هست، پس در نتیجه میتوان از هر راس در \(F(v)\) به هر راس در \(F(u)\) رفت و همچنین از هر راس در \(F(u)\) به هر راس در \(F(v)\) مسیری جهتدار وجود دارد (چرا؟) که یعنی رئوس \(F(v)\) و \(F(u)\) باید در یک مولفه قویا همبند قرار بگیرند که این با فرض ماکسیمال بودن مولفههای قویا همبند در تناقض است. پس در نتیجه \(H\) دور ندارد و حکم اثبات میشود.
طبق قضیه 3.3.2، میتوان رئوس هر گراف فشرده شده مولفههای قویا همبند را توپولوژیک سورت کرد. پس در نتیجه میتوان مولفههای قویا همبند هر گراف جهتداری را به صورت توپولوژیک سورت کنار هم قرار دهیم، یعنی به این صورت که جهت همه یالهایی که بین دو مولفه متفاوت باشند، به یک سمت باشند (شکل 3).
حال قصد داریم الگوریتمی با پیچیدگی زمانی مناسب برای پیدا کردن مولفههای قویا همبند یک گراف ارائه دهیم.
شرح: ابتدا روی کل گراف \(DFS\) میزنیم و هربار هنگام خروج از هر راس آن را در یک پشته میاندازیم (توجه کنید هر چه از راسی دیرتر خارج شویم، در پشته بالاتر قرار میگیرد). حال گراف معکوس را درنظر میگیریم و در هر مرحله از بین تمام راسهای دیده نشده، راسی که در پشته بالاتر از بقیه قرار دارد (مانند \(v\) ) را برداشته و از آن در گراف معکوس، \(DFS\) میزنیم و \(v\) و تمام راسهایی که دیده میشوند (از طریق \(DFS\) زدن از راس \(v\) ) را در یک مولفه جدید قرار میدهیم و این کار را تا پیدا شدن تمام مولفههای قویا همبند ادامه میدهیم. توجه کنید که هنگام \(DFS\) زدن بر روی گراف معکوس، زمانی که راسی دیده میشود، آن را به عنوان دیده شده علامت زده و در سریهای بعد از آن \(DFS\) نمیزنیم و یا به آن وارد نمیشویم.
اثبات درستی: برای اثبات این الگوریتم, ابتدا به لم زیر که مشابه آن را در بخش قبل داشتیم توجه کنید.
تعریف میکنیم \(f(v)\) به معنای زمان تمام شدن پیمایش برای راس \(v\) . به عبارتی داریم موقعیت یک راس را در پشته مشخص میکنیم(هر چه \(f(v)\) بیشتر باشد, راس در پشته در جایگاه پایینتری قرار میگیرد).
لم ۱: اگر \(f(u) > f(v)\) و به عبارتی راس \(u\) در پشته, بالا تر از راس \(v\) باشد, و همچنین مسیری از \(v\) به \(u\) باشد, آنگاه مسیری از \(u\) به \(v\) نیز وجود دارد.
برهان لم۱: از برهان خلف استفاده میکنیم. فرض کنید مسیری از \(v\) به \(u\) است و هیچ مسیری از \(u\) به \(v\) نباشد.
آنگاه چون ما در ابتدا راس \(u\) را در پیمایش دیدهایم(چرا؟) و مسیری از \(u\) به \(v\) نیست, پس هیچگاه راس \(v\) را پیمایش نمیکنیم, مگر اینکه پیمایش راس \(u\) تمام شود. از طرفی اگر پیمایش راس \(u\) تمام شود و هنوز راس \(v\) را ندیده باشیم, نتیجه میشود که اول \(u\) به پشته اضافه میشود و بعد \(v\) به عبارتی \(f(u) < f(v)\) که این خلاف فرض است. پس لم ثابت شد!
حال توجه داشته باشید در هنگام پیمایش گراف معکوس, روی معکوس یالها حرکت میکنیم. یعنی عضو بالای پشته که راس \(v\) باشد را برمیداریم و روی همه راسهایی مثل \(x\) که از \(x\) به \(v\) مسیر هست پیمایش میکنیم. در اینصورت طبق لم۱، راس \(v\) هم به راس \(x\) مسیر دارد!
پس \(v\) و همه رئوسی که در پیمایش گراف معکوس از \(v\) دیده شدند در یک مولفه قرار میگیرند!
از طرفی هیچ راس دیگری در این مولفه نیست. زیرا در غیر این صورت اگر راس دیگری در این مولفه قرار میگرفت، باید حداقل یک مسیر به \(v\) میداشت و جزو رئوس دیدهشده از \(v\) شمرده میشد.
در الگوریتم بالا صرفا ۲ بار از \(DFS\) استفاده کردیم, در نتیجه پیچیدگی الگوریتم برابر با \(O(n + m)\) است که \(m, n\) به ترتیب تعداد راسها و یالها میباشند.
صورت لم: الگوریتم کساراجو، مولفههای قویا همبند را به ترتیب توپولوژیک سورت آنها آنها پیدا میکند.
اثبات: به استقرا بر روی تعداد مولفههای قویا همبند حکم را ثابت میکنیم. به ازای یک مولفه که درستی حکم بدیهی است. حال اگر فرض بر درستی حکم به ازای \(n-1\) مولفه باشد، حکم را به ازای \(n\) مولفه ثابت میکنیم. اگر به مولفه اولی که در الگوریتم پیدا میکنیم (مانند \(H\) ) از مولفه دیگری مانند \(L\) یالی وارد شود، در این صورت در گراف معکوس، از \(H\) به \(L\) یال هست و چون در الگوریتم، \(L\) بعد از \(H\) پیدا میشود، پس در هنگام پیمایش \(H\) در گراف معکوس، باید تعدادی از راسهای \(L\) دیده شوند و در \(H\) قرار گیرند در صورتی که مولفهها نمیتوانند اشتراک داشته باشند. پس در نتیجه مولفه اولی که پیدا میکنیم یالی از سمت مولفههای دیگر به آن وارد نمیشود و اولین مولفه در توپولوژیک سورت میباشد. حال طبق فرض استقرا باقی مولفهها نیز به ترتیب توپولوژیک سورت پیدا میشوند (چرا؟) و حکم اثبات میشود.
توجه کنید انتها کد، مولفه ها را به ترتیب توپولوژیک سورت آنها خروجی میدهیم.
const int MX = 5e5 + 5;
int n, m; /// tedad ras ha va yal ha
vector<int> gr[MX], back_gr[MX], comp[MX]; /// vector mojaverat, vector mojaverat(yal haie barax), moalefe haie ghavian hamband
stack<int> sk; /// moratab kardan ras ha barhasbe tamam shodan dfs
bool mark[MX]; /// array mark baraie check kardane dide shodan ras ha
void dfs(int v){ /// dfs mamoli!
mark[v] = 1;
for(int u: gr[v])
if(!mark[u])
dfs(u);
sk.push(v);
}
void back_dfs(int v, int cnt){ /// dfs roie yal haie barax!
mark[v] = 1;
comp[cnt].push_back(v);
for(int u: back_gr[v])
if(!mark[u])
back_dfs(u, cnt);
}
int main(){
cin >> n >> m;
for(int i = 0; i < m; i++){
int v, u;
cin >> v >> u; /// shomare ras ha 0-based hast.
gr[v].push_back(u); /// in vector yal ha ra zakhire mikonad
back_gr[u].push_back(v); /// in vector barax yal ha ra zakhire mikonad
}
for(int i = 0; i < n; i++)
if(!mark[i])
dfs(i);
fill(mark, mark + n, 0); /// chon mikhahim dfs jadid bezanim, mark ra 0 mikonim.
int cnt = 0;
while(sk.size() != 0){ /// stack kheili kond ast. dar inja serfan baraie dark behtar stack estefade shode. behtar ast az vector estefade konid.
if(!mark[sk.top()]){
back_dfs(sk.top(), cnt); /// yek moalefe ra peida mikonim
cnt++;
}
sk.pop();
}
/// moalefe hara be tartib topo sort eshan chap mikonim
for(int i = 0; i < cnt; i++){
cout << i << ": ";
for(auto v: comp[i])
cout << v << ' ';
cout << endl;
}
return 0;
}