ای سی ام کار تازه وارد

در این وبلاگ مطالبی در مورد برخی الگوریتم ها و راهنمایی برای حل سوالات ارائه می گردد

ای سی ام کار تازه وارد

در این وبلاگ مطالبی در مورد برخی الگوریتم ها و راهنمایی برای حل سوالات ارائه می گردد

Lowest Common Ancestor

چهارشنبه, ۶ فروردين ۱۳۹۳، ۱۰:۵۱ ب.ظ

در یک درخت ریشه دار ، "پایین ترین جد مشترک" دو گره را دورترین راس از ریشه تعریف می کنیم که جد هردو گره باشد .


این مساله معمولا برای درخت های بزرگ مثلا 105 راسی مطرح میشود .


برای حساب کردن ( LCA( x , y به صورت زیر عمل می کنیم .

بدون کاسته شدن از کلیت فرض کنید گره x در ارتفاع بیشتری باشه .

قبل از هر کاری باید گره x رو اونقدر به بالا منتقل کنیم تا هم ارتفاع با y بشه .

حالا اگر x = y باشه در نتیجه LCA همان x است

اگر x != y باشه ، تو این حالت از اونجاییکه فاصله x تا LCA و فاصله y تا LCA برابر است یک مقدار i دلخواه پیدا می کنیم که i امین پدر x و y با هم برابر نباشند ، از اونجایی که 

LCA( x , y ) = LCA( Parent( x , i ) , Parent( y , i ) )

می تونیم قرار بدیم ( x = Parent( x , i ) , y = Parent( y , i و مساله رو برای این زیر مساله حل کنیم .

اگر خوب دقت کنید میبینید که انتخاب i خیلی تو سرعت الگوریتم تاثیر داره .

منظور از ( Parent( x , y یعنی پدر y ام گره x .


ایده کلی ساختمان داده اینه که برای هر گره ، 20 امین پدر ، 21 امین پدر ، 22 امین پدر و ... رو نگه داریم .


حالا برای بدست آوردن k امین پدر یک گره کافیه k رو به صورت باینری نگاه کنیم .

از اونجایی که k = 2i1 + 2i2 + ... + 2in  می تونیم

اول 2i1 امین پدر k رو محاسبه کنیم سپس پدر k - 2iگره جدید رو محاسبه کنیم .

از اونجایی که یه عدد رو میشه به صورت حداکثر Log N تا جمعوند نوشت ، الگوریتم بعد از Log N محاسبه به جواب می رسه .


نحوه ساختن درخت

برای ساختن درخت کافیه برای هر گره بدونید پدرش کیه و تو چه ارتفاعی قرار داره .

برای این کار می تونید درخت از ریشه BFS یا DFS بزنید تا Dep و Parent هر گره رو معلوم کنید .

حالا با داشتن این مقادیر درخت رو می سازیم

for( int i = 0 ; i < N ; i ++ )
    for( int j = 0 ; ( 1 << j ) < N ; j ++ )
        Tree[i][j] = -1;
for( int i = 0 ; i < N ; i ++ )
    Tree[i][0] = Par[i];
for( int j = 1 ; ( 1 << j ) < N ; j ++ )
    for( int i = 0 ; i < N ; i ++ )
        Tree[i][j] = Tree[ Tree[i][j-1] ][j-1];

تو این درخت ، منظور از [Tree[i][j یعنی پدر 2j ام از گره i .

اول پدر همه رو -1 می زاریم ، یعنی اینکه هیچ پدری نداره .
بعد پدر سطح 20 ام رو مشخص می کنیم .

حالا چون به ترتیب از پایین به بالا حرکت می کنیم ، برای برست آوردن پدر 2j ام از راس i کافیه ،

پدر 2j-1 از راس i رو بدست بیاریم ( قبلا محاسبه کردیم و تو خونه [Tree[i][j-1 ذخیره کردیمش ) و بعد پدر 2j-1 ام اون گره رو بدست بیاریم ( این مقدار رو هم قبلا محاسبه کردیم )


برای بدست آوردن LCA دو گره کافیه تابع زیر رو فراخوانی کنیم

int LCA( int x , int y ) {
  if( Dep[x] < Dep[y] )
    swap( x , y );
  int log = 0;
  for( int log = 0 ; ( 1 << log ) < Dep[x] ; log ++ );
  log --;
  for( int j = log ; j >= 0 ; j -- )
    if( ( Dep[x] - Dep[y] ) & ( 1 << j ) )
      x = Tree[x][j];
  if( x == y )
    return x;
  for( int j = log ; j >= 0 ; j -- )
    if( Tree[x][j] != -1 && Tree[x][j] != Tree[y][j] )
      x = Tree[x][j], y = Tree[y][j];
  return Par[x];
}

پیچیدگی زمانی:
( O( N * Log N برای پیش محاسبات و ساختن درخت
( O( Log N برای بدست آوردن هر Query

حافظه : ( O( N * Log N 

موافقین ۱ مخالفین ۰ ۹۳/۰۱/۰۶
رضا حسینی آشتیانی LCA

نظرات  (۲)

سلام
میشه سؤالی رو مثال بزنید که با این روش حل میشه؟
پاسخ:
http://www.spoj.com/problems/LCA/
خیلی متنت دقیق و کامل بود ولی یه بار کدتو ران کن ایراد داره.
پاسخ:
ممنون
درستش کردم، فقط مشکل کامپایل نشدن منظورت بود دیگه؟؟

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی