خبرگاه المپیاد کامپیوتر
• منبع رسمی اخبار و اطلاعیه‌های کمیته‌ی المپیاد کامپیوتر در ایران
خبرگاه
آخرین خبر
اوّلین آزمون اینترنتی سایت acm.sharif.edu برگزار می‌شود.
پیوندها
تماس با کمیته
برای تماس با کمیته‌ی المپیاد کامپیوتر، نامه‌های الکترونیکی خود را به نشانی زیر ارسال نمائید:
سؤالات مفید در همین صفحه پاسخ داده خواهند شد.
 
Top درباره:
•  در این صفحه آخرین اخبار و اطلاعیه‌های مربوط به المپیاد کامپیوتر ایران نگاشته می‌شود.
•  کلیه‌ی نوشته‌جات این صفحه مورد تأیید کمیته‌ی ملی المپیاد کامپیوتر می‌باشد .
 
Top درس‌ها و اطلاعیه‌ها:
•  در این بخش دروس و اطلاعیه‌های مربوط به دانش‌پژوهان المپیاد کامپیوتر نگاشته می‌شود.
معرفی چند اسلاید و کتاب مناسب در زمینه الگوریتم‌های لازم در المپیاد


کتاب‌ها و اسلاید‌های زیر به دانش‌پژوهان دوره‌ی زمستانه (دارندگان مدال نقره یا طلا) و علاقه‌مندان توصیه می‌شود.
 
اولین مسابقه برنامه‌سازی ای‌سی‌ام دانش‌آموزی


اوّلین مسابقه برنامه‌سازی ای‌سی‌ام دانش‌آموزی، امسال به‌صورت اینترنتی و (گویا) سپس در دانشگاه شریف برگزار می‌شود. برای ثبت و اخبار بیش‌تر و دقیق‌تر به وبلاگ مسابقه‌ی ای‌سی‌ام مراجع کنید.
 
اطلاعیه: مسابقه‌ی آنلاین ۲۸ اسفندماه ۱۳۸۵


بدین‌وسیله به اطلاع کلیّه‌ی علاقه‌مندان می‌رساند، در روز دوشنبه ۲۸ اسفندماه ۱۳۸۵ از ساعت ۹ الی ۱۴ یک آزمون آزمایشی برنامه‌نویسی در سایت acm.sharif.edu برگزار می‌شود. شرکت در این آزمون به شرکت‌کنندگان در دوره‌ی فروردین‌ماه توصیه می‌شود.
 
درس: Segment Tree


این درس شامل موارد زیر است:
مقدمه
در این مقاله میخواهیم ضمن حل چند مساله با یک Data Structure جدید آشنا شویم.
مساله ی اول
n عدد با نام های A1 … Anرا در نظر بگیرید که درابتدا همگی 0 هستند. هربار یکی از دو عمل زیر را روی دنباله میتوانیم انجام دهیم :
    1) Add(i , x) : این عمل به Ai ، به اندازه x تا اضافه میکند.
    2) Sum(i , j) : این عمل از شما می خواهد تا مجموع را در خروجی چاپ کنید.
خوب، یک الگوریتم بدیهی برای حل این مساله این است که یک آرایه به طول n (مثلن به نام a) در نظر بگیریم،
به ازای هر عمل Add(i , x) به a[i] ، x تا اضافه کنیم و برای هر سوال Sum(i , j) ، روی خانه های i ام تا j ام از آرایه for ببندیم و مجموع خواسته شده ( ) را در خروجی چاپ کنیم .
به این ترتیب هر عمل Add را در زمان O(1) و هر عمل Sum را در O(n) انجام میدهیم. و انجام m عمل روی دنباله از O(nm) طول خواهد کشید .
خوب ، پاسخ به m سوال در O(nm)) ... . به عنوان تمرین کمی فکر کنید و سعی کنید تا Order الگوریتم را کاهش دهید. (راهنمایی :آرایه a را به قسمت مساوی تقسیم کنید. به این ترتیب میتوانیم Order الگوریتم را به کاهش دهیم.)
با استفاده از Data Structure ای به نام Segment Tree میتوان Order الگوریتم را تا m log n کاهش داد :
یک درخت دودوای ریشه دار میسازیم و به هر راس آن یک زوج مرتب نسبت میدهیم .درخت را این گونه میسازیم :
به ریشه زوج [1, n] را نسبت میدهیم واز این به بعد این برای هر راس vبا زوج [i , j] داریم :
  • اگر i = j ، v هیچ بچه ای نخواهد داشت .
  • اگر i != j ، v یک بچه ی چپ با زوج مرتب [ i , (i+j)/2 ] و یک بچه راست با زوج مرتب [ (i+j)/2 +1 , j] خواهد داشت.
  • برای مثال اگر n=4 ، درخت شرح داده شده در شکل شماره 1 آمده است

حال برگردیم به مساله ی اول :
اگر چنین Data Stcuture ای داشته باشیم ، جواب دادن سوال Sum(i , j) با O(log n) امکان پذیر است :
یک آرایه به نام s در نظر میگیریم که برای هرراس v در درخت با زوج [i , j]، s[v] برابراست با .
(برای راحتی کار ، از این به بعد ، مولفه ی اولِ زوج مرتبِ راس دلخواهِ v از درخت را با v.left و مولفه ی دوم آن را با v.right نشان میدهیم.)
if ( i <= v.left && v.right <= j) return s[v] return find_sum(v.left , i, j) + find_sum(v.right, i, j);

int find_sum(v, i, j)

    if (v.right < i || j < v.left)

        return 0
    if (i <= v.left && v.right <= j)

        return s[v]
    return find_sum(v.left, i, j) + find_sum(v.right, i, j)

حال، برای جواب دادن به سوال Sum(i , j) کافیست تا find_sum(root, i, j) را در خروجی چاپ کنیم .
ادعا میکنیم که find_sum(v, i, j) که یک الگوریتم بازگشتی است، مجموع Ak هایی را بر میگرداند که


اثبات درستی الگوریتم :
find_sum(v, i, j) برای پیدا کردن این گونه عمل میکند :
اگر بازه ی [v.left , v.right] زیر بازه ی [i , j] بود ، s[v] را به عنوان جواب بر میگردانیم، وگرنه بازه ی [v.left , v.right] را به دو بازه ی جدا از هم Aleft=[v.left , (v.left+v.right)/2] و Aright=[(v.left+v.right)/2 +1 , v.right] تقسیم میکنیم، واضح است که پس :

find_sum(v,i,j) = find_sum(v.left,i,j) + find_sum(v.right,i,j)

پس find_sum(root, i, j) ، را بر میگرداند.
تمرین : ثابت کنید که زمان اجرایfind_sum(root, i, j) از O(log n) است.
خوب، تا به حال ثابت کرده ایم که اگر برای هر v ، s[v] نشان دهنده باشد،
find_sum(root, i, j) sigma Ai را بر میگرداند ، برای اینکه الگوریتممان کامل شود، کافیست طوری عمل کنیم تا بعد از هر بار عمل Add ، s[v] نشان دهنده Simgma باشد.
در ابتدا Ai ها 0 اند، پس برای هر v باید داشته باشیم s[v] = 0 . بعد از Add(i , x)، s را اینگونه update میکنیم :
void add(v, i, j)

    s[v] += x
    if (v.left == v.right )

        return
    if (v.left <= i && i <= (v.left+v.right)/2)

        add(v.left, i, x)
    else

        add(v.right, i, x)
به این ترتیب واضح است که برای انجام دستور add(i, x) و update کردن s ، کافیست تا add(root, i, x) را صدا بزنیم.
با توجه به اینکه ارتفاع درخت از O(log n) است (چرا؟) زمان اجرای add(root, i, x) هم از O(log n) است.
به این ترتیب ما در هر عمل Add با O(log n) ، s را update کردیم و به هر سوال Sum هم با O(log n) جواب دادیم، در نتیجه الگوریتممان از O(m log n) خواهد بود.

مساله ی دوم
n عدد با نام های A1 … Anرا در نظر بگیرید که درابتدا همگی 0 هستند. هربار یکی از دو عمل زیر را روی دنباله میتوانیم انجام دهیم :
    1) Add(i , x) : این عمل Ai را مساوی Ai+x قرار میدهد. ( x مقداری مثبت است )
    2) MaxOf(i , j) : این عمل از شما می خواهد تا عدد ماکسیمم بین Ai , Ai+1 , … , Aj را در خروجی چاپ کنید.
باز هم از Segment Tree استفاده میکنیم، درخت شرح داده شده در مساله ی اول را تشکیل میدهیم، یک آرایه به اسم max هم در نظر میگیریم به طوری که برای هر
راس v ، max[v] برابر با ماکسیمم عدد در بین Av.left , Av.left+1 , … , Av.right باشد.
همانند مساله ی اول عمل خواهیم کرد، فرض کنیم که آرایه max خاصیت گفته شده را دارد، در این صورت به هر سوال MaxOf(i , j) اینگونه پاسخ میدهیم :
int maxof(v, i, j)

    if (v.right < i  ||  j < v.left)

        return 0
    if (i <= v.left && v.right <= j)

        return max[v]
    return maximum ( maxof(v.left, i, j) , maxof(v.right, i, j) )

حال برای پاسخ دادن به سوال MaxOf(i , j) کافیست تا MaxOf(root, i , j) را در خروجی چاپ کنیم. در ضمن با توجه به مساله ی یک بدیهیست که زمان اجرای MaxOf(root, i , j) از O(log n) است. با توجه به اینکه همه Ai ها در ابتدا 0 اند، پس در آغاز کار برای هر راس v داریم max[v] = 0 . اکنون تنها چیزی که باقی میماند این است که max را پس از هر
بار عمل Add ، update کنیم :
void add(v, i, j)
    max[v] = maximum(max[v], A_i + x)

    if (v.right == v.left)
        /* here v.left is equal to i */

        A_i += x
        return
    if (v.left <= i && i <= (v.left+v.right)/2)

        add(v.left, i, x)
    else

        add(v.right, i, x)
پس به این ترتیب، باز هم هز سوال را در O(log n) جواب دادیم و در نتیجه الگوریتم از O(m log n) میباشد.
مساله ی سوم
همان مساله ی اول با این تفاوت که عمل Add را اینگونه تغییر میدهیم :
Add(i, j, x) : برای هر k، (i <= k <=j) به Ak ، x تا اضافه میکنیم.
برای حل این مساله ، همانند مساله ی اول عمل میکنیم با این تفاوت که یک آرایه دیگر به نام all نگه میداریم که all[v] برابر است با مقداری که قرار است به
همه ی Av.right Av.left… افزوده شود :
int find_sum(v, i, j){
    if (v.right < i || j < v.left)  
        return 0

    if ( i <= v.left  &&  v.right <= j)

        return s[v]
    int eshterak = minimum(v.right, j) - maximum(v.left, i) + 1

    /* length of the interval [v.left, v.right] ^ [i, j] */
    return  find_sum(v.left , i, j) 
                 + find_sum(v.right, i, j) + all[v] * eshterak

}

void add(v, i, j, x){

    if (v.right < i || j < v.left)  
        return 
    if ( i <= v.left &&  v.right <= j)

        all[v] += x
    else {
        add(v.left, i, j, x)

        add(v.right, i, j, x)
    }

    s[v] = s[v.left] + s[v.right] + all[v]*(v.right - v.left + 1)
}

اثبات درستی این الگوریتم و محاسبه ی زمان اجرای آن ( که O(m log n) است) به خواننده واگذار میشود.
تمرین
  1. مساله ی 2 با این تفاوت که در تابع Add ، x میتواند منفی هم باشد.
  2. مساله ی 311 از سایت http://acm.sgu.ru
  3. مساله ی 325 از سایت http://acm.sgu.ru
منبع
برای مطالعه بیشتر در این زمینه می توانید به کتاب ورودی به الگوریتم نوشته CLRS و ورودی به الگوریتم با نگرشی خلاقانه مراجعه کنید.
 
درس: شارش بیشینه در شبکه


این درس شامل موارد زیر است:
تعاریف
  • شبکه جریان: شبکه هاي جريان مانند شبکه هاي توزيع آب, حمل و نقل, جريان برق و ... را مي توان مانند خيلي از موارد با گراف مدل سازي کرد. يک شبکه جريان شامل تعدادي گره (رئوس گراف) و ارتباط هاي بين بعضي از آنها (يالهاي گراف) است.
    اين ارتباط ها مانند لوله هاي آب مي توانند جريان را از يک رأس به رأس ديگر منتقل کنند. براي اين يالها محدوديتي وجود دارد که نشان دهنده حداکثر توان آن يال براي عبور جريان است. حداکثر جرياني که از آن يال مي گذرد را با اين محدوديت که آن را ظرفيت (capacity) مي ناميم نشان مي دهيم.
    در يک شبکه جريان به وضوح جريان در رأسي از بين نمي رود. يعني ميزان جريان ورودي يک رأس با جريان خروجي همان رأس برابر است. به اين خاصيت براي يک رأس بقاي جريان در آن رأس مي گويند.
  • جريان در يک شبکه: يک شبکه را با يک گراف جهت دار که هر يال آن وزني دارد نشان داديم. منظور از يک جريان در يک شبکه, نسبت دادن اعدادي نامنفي به يالها به عنوان ميزان جريان عبوري از آن يال است که داراي خواصي که گفته شد باشند. يعني عددي که ما نسبت مي دهيم از ظرفيت يال بيشتر نشود و در رئوسي که مولد يا مصرف کننده جريان نيستند بقاي جريان را داشته باشيم.
    براي بيان دقيقتر مي توان اينطور گفت :
    گراف جهتدار G داراي مجموعه رئوس V ومجموعه يالهاي E است و تابع c روي مجموع يالها تعريف شده که c(e) نشان دهنده ظرفيت آن يال است.
    تابع u را در نظر بگيريد که u(e,v) نمايش دهنده وضعيت يال e و رأس v است. u(e,v) مي تواند سه مقدار 0 و 1 و -1 را به ترتيب در حالتهاي زير بگيرد :
      1. اگر v هيچ يک از دو سر e نباشد u(e,v) را 0 تعريف مي کنيم.
      2. اگر e از رأس v خارج شده باشد u(e,v) را 1 تعريف مي کنيم.
      3. اگر e به رأس v داخل شده باشد u(e,v) را -1 تعريف مي کنيم.
    در اين حالت يک جريان, تابع f روي يالها است که براي هر يال e داشته باشيم و براي هر رأس v که مولد يا مصرف کننده نيست داشته باشيم (حاصل جمع دقيقاً برابر ميزان جريان خروجي منهاي ميزان جريان ورودي است)
    براي مثال شکل زير جرياني در يک شبکه را نشان مي دهد. رئوس مشخص شده مولد يا مصرف کننده هستند و قانون بقاي جريان براي آنها صادق نيست. اعداد کمرنگ روي يالها نمايش دهنده جريان عبوري از آن يال و اعداد پررنگ نمايش دهنده ظرفيت آن يال است.


مسئله بيشينه جريان
شبکه جرياني را در نظر بگيريد که در آن فقط دو رأس استثناء يعني رئوسي که مولد يا مصرف کننده هستند وجود دارند. اين دو رأس را با s و t نشان مي دهيم. s به عنوان منبع توليد جريان (چشمه) و t به عنوان مصرف کننده (چاه) در نظر گرفته مي شوند.
مسئله ما شامل پيدا کردن جرياني در شبکه است که بيشترين ميزان ممکن جريان را از s به t منتقل کند. يعني جرياني که ميزان خروجي s منهاي ميزان ورودي s در آن بيشينه باشد.
ديدگاه فيزيکي زير به ما براي حل اين مسئله کمک مي کند :
    در يک جوي آب حداکثر جريان عبوري از جوي با کمترين سطح مقطع جوي متناسب است.
در يک شبکه جريان نيز کمابيش وضعيت مانند بالاست.
برش ها در شبکه جريان
يک برش (cut), افراز رئوس گراف به دو مجوعه S و T است که منبع در مجموعه S و چاه در مجموعه T قرار بگيرند. تعبير برش در مثال جوي آب, در نظر گرفتن دو تکه در دو سمت سطح مقطع جوي است.
مثال جوي آب را در نظر بگيريد، اگر دو سطح مقطع در جوي را در نظر بگيريد آب در تکه جوي که بين اين دو سطح مقطع قرار مي گيرد نمي تواند انباشته يا توليد شود, يا به عبارت ديگر جريان عبوري از يک سطح مقطع با ديگري برابر است. اين حکم در مورد شبکه هاي جريان هم معادل خود را دارد. جريان عبوري از يک برش را به شکل زير تعريف مي کنيم :


مي خواهيم ثابت کنيم که اين حاصل جمع از برش مستقل است. در واقع


تساوي آخر در بالا به اين خاطر است که اگر دو سر e در T باشند همه جملات u(e,v)f(e) برابر 0 مي شوند, اگر e از T به S باشد هم جمع جملات u(e,v) برابر -f(e) مي شود و اگر هم از S به T باشد اين حاصل جمع برابر f(e) مي شود, اگر هم از S به S باشد مثلاً e=(a,b) اين حاصل جمع برابر u(e,a)f(e)+u(e,b)f(e) يا f(e)-f(e)=0f(e)-f(e)=0 مي شود. پس تساوي بالا برقرار است. اما


تساوي آخر به اين علت برقرار است که جمله حاصل جمع داخلي براي x هايي که در آنها قانون بقاي جريان برقرار است بنابر تعريف 0 مي شود و فقط براي s از بين نمي رود.
اما سمت راست تساوي به برش ما ربطي ندارد و با نگاه دقيقتر مي بينيم که برابر جريان خارج شده از s است, همان چيزي که ما مي خواهيم بيشينه اش کنيم.
حالا مي توانيم يک طرف قضيه اي را که مي خواستيم، ثابت کنيم. يعني اينکه جريان خارج شده از s از ظرفيت هر برشي کمتر يا مساوي است. منظور از ظرفيت يک برش است. چون مقدار جريان برابر است با


براي اينکه نشان دهيم براي يک برش تساوي رخ مي دهد, از الگوريتمي که در ادامه مي آيد استفاده مي کنيم.
الگوريتم پيدا کردن جريان بيشينه
براي راحتي کار فرض کنيد ظرفيت يالهايي که در گراف نيستند را 0 در نظر مي گيريم (در تساوي هاي قبل به طور ضمني از اين موضوع استفاده کرده بوديم)
از روي يک جريان مانند f مي توان تابع F را ساخت که . F در حقيقت جريان واقعي بين دو رأس را نشان مي دهد و مي تواند منفي هم بشود. براي F بايد رابطه برقرار باشد. در ضمن شرط بقاي جريان براي F هم به صورت مي شود. از روي هر تابع F با اين خواص نيز مي توان تابع f را به صورت ساخت. به عنوان تمرين مي توانيد موارد زير را ثابت کنيد.
  • f اي که به اين صورت ساخته مي شود يک جريان است.
  • جريان خروجي ازs در f برابر است
  • اگر (S,T) يک برش باشد آنوقت
به وضوح يک جريان براي هر شبکه وجود دارد, و آن, جريان همه جا 0 است. در ابتدا تابع F را همه جا برابر 0 قرار دهيد.
اگر مسيري از s به t وجود داشته باشد که ظرفيت يالهاي آن ناصفر باشند مي توان به F يالهاي اين مسير مقداري اضافه کرد و از F يالهاي مسير معکوس همان مقدار را کم کرد. در واقع مقدار اضافه شده بايد از تمام ظرفيت هاي يالهاي اين مسير کمتر باشد, پس به اندازه کمترين ظرفيت يالهاي اين مسير به F تمام يالها اضافه مي کنيم و همين مقدار را از يالهاي مسير معکوس کم مي کنيم. با اينکار جرياني با اندازه بيشتر به دست مي آوريم. بعد از اين عمل ظرفيت يالها کمي تغيير مي کند. در واقع بايد از ظرفيت تمام يالهاي اين مسير مقدار اضافه شده به F را کم کرد و به ظرفيت يالهاي مسير معکوس اين مقدار را اضافه کرد.
با انجام عمل بالا جرياني با اندازه خروجي از s بيشتري به وجود مي آيد. پس عمل بالا را مي توان تا جاي ممکن انجام داد. در پايان گرافي به وجود مي آيد که در آن هيچ مسيري با ظرفيت يالهاي مثبت از s به t وجود ندارد. (ممکن است الگوريتم پايان نپذيرد ولي در تمرينات مي بينيم که براي حالتي که وزن يالها صحيح باشند حتماً الگوريتم پايان مي پذيرد)
در گراف حاصل مي توان برشي را به صورت زير تعريف کرد :
    S را تمام رئوسي قرار دهيد که از s به آنها مسيري با يالهاي داراي ظرفيت مثبت وجود دارد. T را هم بقيه رئوس بگيريد.
در تمرين ها مي بينيد که در الگوريتم بالا F(x,y)+c(x,y) براي دو رأس x و y همواره ثابت است. در برش ما اگر x در S و y در T باشند چون c(x,y)=0 پس F(x,y) برابر مقدار اوليه c(x,y) است. پس به راحتي ديده مي شود که براي اين برش پس جرياني پيدا کرديم که برابر يکي از برش هاست (از نظر مقداري) ولي چون هر جرياني از هر برشي کمتر يا مساوي است پس اين جريان بيشينه است.
کاربردهاي الگوريتم جريان بيشينه
يکي از مثالهاي کاربرد اين الگوريتم مسئله تطابق بيشينه است. در گراف دو بخشي (X,Y) مي خواهيم بيشترين تعداد يالهاي ممکن که با هم رأس مشترک ندارند را پيدا کنيم.
تمام يالهاي گراف دو بخشي را از بخش X به بخش Y جهت دار کنيد و به گراف دو رأس s و t را اضافه کنيد.
از s به تمام رئوس X و از تمام رئوس Y به t يال بگذاريد. وزن تمامي يالها را نيز 1 تعريف کنيد


طبق الگوريتم ما در گرافي که وزن تمام يالهاي آن صحيح هستند مي توان جرياني پيدا کرد که در آن جريان عبوري از هر يلل يک عدد صحيح شود (چون در هر مرحله از الگوريتم ما مقدار صحيحي جريان هر يال را تغيير مي دهيم) پس در جريان حاصل جريان هر يال 0 يا 1 است.
برشي را در نظر بگيريد که S آن شامل s و رئوس X است و T شامل t و رئوس Y. طبق لمي که داشتيم


و مقدار سمت راست برابر تعداد يالهاي داراي جريان بين X و Y است. از طرفي هيچ دو تا از اين يالها نمي توانند در يک رأس مشترک باشند چون جريان ورودي به رئوس X و جريان خروجي از رئوس Y حداکثر 1 هستند و در نتيجه دو يال جريان دار نمي توانند از يک رأس X خارج يا به يک رأس Y وارد شوند. به راحتي مي توان ديد به ازاي هر تطابق نيز يک جريان وجود دارد. از طرفي چون تعداد يالها برابر مقدار جريان است پس تطابق وقتي بيشينه است که جريان متناظرش بيشينه باشد.
در تمرين ها کاربردهاي بيشتري از اين مسئله را مي بينيم.
پياده سازي الگوريتم
در زير شبه کد اين الگوريتم آمده است
maxflow(Graph G, Cost c, source s, sink t)

    for each edge(x, y) in E(G) do

        F[x, y] := 0
        F[y, x] := 0

    while there is a positive capacity path P in G from s to t do
        k := { min c(e) | for every edge e in P }

        for each edge(x, y) in P do
            F[x, y] := F[x, y] + k
            F[y, x] := F[y, x] - k
            F[x, y] := F[x, y] - k
            F[y, x] := F[y, x] + k


در شبه کد بالا براي پيدا کردن مسير مي توان از الگوريتم هايي مانند DFS و BFS استفاده کرد. البته اگر از BFS استفاده شود مي توان ثابت کرد که الگوريتم در پايان مي پذيرد. از اينرو الگوريتم پيدا کردن جريان بيشينه در رده الگوريتم هایي با زمان اجراي چندجمله اي جاي مي گيرد. (رجوع شود به [CLRS])
تمرين ها
در تمرين هاي زير تعداد ستاره ها مشخص کننده سختي است و تمرين هاي بدون ستاره براي ياد آوري و يا اثبات احکام جا مانده در متن است.
  1. ثابت کنيد در مراحل انجام الگوريتم مقدار F(x,y)+c(x,y) براي دو رأس x و y تغير نمي کند.
  2. ثابت کنيد اگر ظرفيت ها اعدادي صحيح باشند الگوريتم در |f*| مرحله که f* جريان بيشينه است پايان مي پذيرد. با استفاده از اين موضوع و يافتن کران خوبي براي |f*| (از روي برشي که در آن S فقط تک رأس s را دارد) نشان دهيد که الگوريتم براي تطابق بيشينه در پايان مي پذيرد.
  3. * فرض کنيد ظرفيت يالها بتواند بينهايت شود, نشان دهيد اگر جريان بيشينه بينهايت نشود همچنان الگوريتم پايان پذير است. از اين موضوع استفاده کنيد و الگوريتم را طوري تغيير دهيد که در صورتي که جريان بيشينه بينهايت شود در متناهي مرحله به ما اطلاع دهد که جريان بيشينه بينهايت است و در غير اينصورت به ما جريان بيشينه را برگرداند.
  4. * نشان دهيد وقتي شبکه ما چند يال از يک رأس به رأس ديگر دارد با حالتي که دقيقاً يک يال از هر رأس به رأس ديگر وجود دارد معادل است و از آن نتيجه بگيريد که مي توان الگوريتم را در زمان اجراي پياده سازي کرد.
  5. * ثابت کنيد که مسأله پيدا کردن جريان بيشينه در حالتي که چند رأس چشمه و چند رأس چاه وجود دارند با حالتي که فقط يک چاه و يک چشمه وجود دارند معادل است (راهنمايي : يک چشمه را به بقيه وصل کنيد و ...)
  6. * نشان دهيد که مسأله وقتي که براي جريان عبوري از هر رأس نيز ظرفيت دارد با حالتي عادي معادل است. (راهنمايي : هر رأس را دو تا کنيد, يکي رأس ورودي و ديگري رأس خروجي)
  7. ** نشان دهيد که عدد همبندي يالي يک گراف را مي توان با چند بار استفاده از maximum flow حل کرد (راهنمايي : تعداد يالهاي لازم براي حذف کردن براي اينکه از رأس ثابت u به رأسي ديگر مسير پيدا نشود را با استفاده از الگوريتم روي گراف به اضافه يک رأس مجازي t پيدا کنيد)
  8. اگر تعداد يالها زياد باشد بايد آنها در ليست مجاورت نگهداري کنيم. در اين حالت بگوييد بايد به چه نکاتي در پياده سازي توجه کرد. (راهنمايي : يالهاي مسير معکوس را چگونه پيدا مي کنيد؟)
  9. ثابت کنيد اگر در شبکه اي ظرفيت هر يال 0 يا 1 باشد, جريان بيشينه اي وجود دارد که جمع تعدادي مسير مجزاي يالي است.
  10. * نشان دهيد کمينه تعداد يالهاي لازم که اگر آنها را حذف کنيم از رأس x به رأس y در گراف مسيري وجود نداشته باشد با حداکثر تعداد مسيرهاي مجزاي يالي بين x و y برابر است.
  11. *** يک پوشش مسيري از گراف G تعدادي مسير مجزاي رأسي در G است که تمام رئوس G در آنها ظاهر شده اند. ثابت کنيد که پوشش مسيري با تعداد مسيرهاي کمينه را مي توان در صورتي که گراف يک DAG باشد را با maximum flow پيدا کرد. (راهنمايي : گراف دو بخشي بسازيد که هر بخش آن رئوس G باشند و يالهاي آن از x در بخش بالا به y در بخش پايين است اگر در گراف G از x به y يال وجود داشته باشد.) نشان دهيد که اين الگوريتم براي گراف هاي داراي دور کار نمي کند.
  12. **** فرض کنيد روي جريان عبوري از يک يال يک کران پايين نيز قرار دهيم. با ديدن تعريف F از روي f و رابطه نشان دهيد که چطور شبکه اي با اين خاصيت بسازيم (راهنمايي: از ظرفيت هاي منفي استفاده کنيد). مي خواهيم اين مسأله را که جرياني در شبکه با ظرفيت هاي منفي پيدا کنيم را حل کنيم. (در اين مسأله s و t اي وجود ندارند) نشان دهيد که مسأله پيدا کردن جريان بيشينه با استفاده مکرر از مسأله بالا قابل حل است (راهنمايي : از t به s يک يال با ظرفيت بينهايت قرار دهيد و روي ميزان جريان جستجوي دودويي را انجام دهيد) فرض کنيد گراف مسأله جديد G باشد. گراف H را اينطور بسازيد که و ظرفيت يالهاي H را که با d نمايش مي دهيم را اينطور تعريف مي کنيم که براي هر u و v در V(G) و و که در آن منظور از c(X,Y) که X و Y دو زيرمجموعه V(G) هستند برابر تعريف مي شود. نشان دهيد که يک جريان در G وجود دارد اگر و فقط اگر تمامي يالهاي H نامنفي باشند و در جريان بيشينه H تمام يالهاي ورودي t اشباع شده باشند (يعني جريان عبوري از آنها با ظرفيتشان يکي باشد) در ضمن از روي اين جريان براي H جرياني براي G بسازيد. نشان دهيد که اگر G خود داراي چشمه و چاه باشد مي توان با دو بار انجام maximum flow جريان بيشينه را در صورت وجود در G پيدا کرد (با فرض اينکه يالها منفي هم مي توانند باشند)
منبع
برای مطالعه بیشتر در این زمینه می توانید به کتاب های زیر مراجعه کنید.
 
درس: تطابق ماکسیمم در گراف دوبخشی


این درس شامل موارد زیر است:
تعاریف
  • گراف دوبخشی: گرافی است که راس‌هایش را بتوان به دو مجموعه‌ افراز کرد، به طوری که برای هر یالِ u، uv و v در دو مجموعه‌ی متفاوت باشند.
  • تطابق: یک تطابق در گراف بدون جهت G عبارت است از مجموعه‌ای از یال‌های گراف به طوری که هر رأس حداکثر به یکی از یال‌های مجموعه متصل باشد. رأسی که به یکی از یال‌های تطابق متصل باشد را «آلوده» می‌نامیم. اگر یک تطابق تمام رئوس گراف را آلوده کند، آن تطابق را یک «تطابق کامل» می‌نامیم.
  • تطابق ماکسیمم: تطابقی است که بیشترین تعداد یال را در گراف داشته باشد. هم‌چنین تطابق ماکسیمال تطابقی است که نتوان یالی از یال‌های گراف را به آن اضافه کرد به طوری که یک تطابق بزرگتر بوجود بیاید. بدیهی است که یک تطابق ماکسیمال ممکن است ماکسیمم نباشد.
  • مسیر متناوب: مسیری است که یال‌هایش یکی در میان در تطابق قرار داشته باشند.
  • مسیر افزایشی: مسیری متناوب که رئوس اول و آخر آن در تطابق نباشند را یک مسیر افزایشی می گویند.
مقدمه
در مجموعه‌ای از اشیا هر شی می‌تواند با برخی از اشیای دیگر سازگار باشد. مثلاً در مجموعه‌ای از آدم ها افراد می‌توانند با یکدیگر دوست باشند در نتیجه می توان دوستی را یک رابطه سازگاری تعریف کرد. حال فرض کنید بخواهیم این گروه را به مجموعه‌هایی دوتایی تقسیم کنیم که هر فرد حداکثر در یک گروه بیاید و افراد هر گروه با یکدیگر دوست باشند. در واقع مسائلی از این قبیل را می‌توان به دید گراف و پیدا کردن یک تطابق ماکسیمم در این گراف نگاه کرد.
با افزایش سؤالاتی از این قبیل دغدغه‌ای برای پیدا کردن یک الگوریتم مناسب برای حل این گونه مسائل در افراد بوجود آمد. از اولین قضایا در این مجموعه که برای پیدا کردن یک الگوریتم مناسب پرکاربرد است، می‌توان به قضیه زیر اشاره کرد:
قضیه ۱
یک تطابق در یک گراف ماکسمیم است، اگر و فقط اگر گراف دارای هیچ مسیر افزایشی‌ای نباشد.
در واقع با شرط لازم بودن این شرط به ما کمک می‌کند برای پیدا کردن یک تطابق ماکسیمم از یک تطابق M که در ابتدا خالی است شروع کرده و با پیدا کردن مسیرهای افزایشی تطابقمان را بزرگ کنیم. به این ترتیب که یک مسیر افزایشی پیدا کرده و یال‌هایی از آن را که در M هستند از M خارج کنیم و یال‌هایی از آن را که در M نیستند به آن اضافه کنیم.
به این ترتیب با وجود این‌که شرط تطابق بودن M از بین نرفته یکی به تعداد یال‌های M اضافه شده است. می توان این کار را آن‌قدر انجام داد تا هیچ مسیر افزایشی دیگری پیدا نشود که در این صورت طبق قضیه 1 می‌توان نتیجه گرفت که M ماکسیمم است.
تنها چیزی که باقی می ماند پیدا کردن مسیر افزایشی در یک گراف G می باشد که ما در اینجا الگوریتمی برای پیدا کردن این مسیرها در گراف دوبخشی می‌دهیم
الگوریتم
الگوریتم پیدا کردن مسیر افزایشی در گراف دوبخشی:
  • ورودی: یک گراف دوبخشی با افراز X و Y از رأس‌ها و یک تطابق M در G و مجموعه‌ی U از همه رأس‌های آلوده نشده توسط M در X.
  • ارزش‌دهی آغازی: S = U و T = Ø.
  • تکرار: برای هر راس در داخل S تمام مسیرهای افزایشی که از راس مورد نظر شروع میشوند را در نظر می گیریم. اگر راسی در بخش X در یکی از این مسیرهای افزایشی قرار داشت آن راس را به مجموعه S اضافه می کنیم. همچنین اگر راسی در بخش Y در یکی از این مسیرهای افزایشی قرار داشته باشد آن راس را نیز به مجموعه T اضافه می کنیم. برای راس های S آنقدر این کار را انجام می دهیم تا هیچ راسی را نتوان به S یا T اضافه کرد.
    حال اگر در مجموعه T راسی باشد که آلوده نشده است در این صورت یک مسیر افزایشی پیدا کرده ایم. زیرا از یکی از راس های غیر آلوده که در مجموعه U قرار داشت به یک راس غیر آلوده در بخش دیگر مسیری متناوب پیدا شده است. چون ابتدا و انتهای این مسیر راس هایی غیر آلوده هستند پس این مسیر یک مسیر افزایشی است.


این کار را آنقدر انجام میدهیم تا زمانی که دیگر مسیر افزایشی در گراف وجود نداشته باشد. توجه داشته باشید که اگر مسیری افزایشی در گراف وجود داشته باشد، اگلوریتم ما آن را پیدا می کند. زیرا الگوریتم ما همه مسیرهای متناوب که از راسی غیر آلوده از مجموعه X شروع می شود را جستجو می کند.
تحلیل
در واقع در این الگوریتم S مجموعه رئوسی در X بودند که با مسیری متناوب از U به آن‌ها رسیدیم و T مجموعه رئوسی در Y بودند که با مسیری متناوب از U به آن‌ها رسیدیم. در واقع پیاده سازی این الگوریتم بوسیله الگوریتم DFS به سادگی قابل انجام است با ادامه دادن این الگوریتم بعد از پیدا کردن هر مسیر افزایشی تا وقتی که دیگر هیچ مسیر افزایشی دیگری در گراف باقی نماند می توان یک تطابق ماکسیمم را در G پیدا کرد.
این الگوریتم در واقع از مرتبه‌ی O(ne) می باشد.
الگوریتم‌های بهتری نیز برای پیدا کردن تطابق دوبخشی ماکسیمم وجود دارد. مثلاً یون و تارجان الگوریتمی دادند که این مسئله را در O(√ne)حل می کند.
مجموعه مستقل
مجموعه مستقل در گراف G به مجموعه ای از راس ها گفته می شود که بین آنها یالی نباشد. یک مجموعه مستقل ماکسیمم مجموعه مستقلی با بیشترین تعداد راس ممکن است. طبق چیزی که در تمرین شماره 4 ثابت می شود می دانیم که اندازه بزرگترین مجموعه مستقل در گراف دوبخشی برابر n ( تعداد راس های گراف ) منها اندازه بزرگترین تطابق است. پس اگر ما موفق به پیدا کردن مجموعه مستقلی به این اندازه شویم مجموعه مستقل ماکسیمم را پیدا کرده ایم.
برای این کار فرض کنید تطابق ماکسیمم را پیدار کرده ایم و این تطابق مجموعه S از رئوس بخش X و T از رئوس بخش Y را آلوده کرده باشد. حال تمام مسیرهای متناوب که از رئوس مجموعه X-S شروع می شوند را در نظر بگیرید. رئوسی از بخش X را که عضو حداقل یکی از این مسیرها هستند را مجموعه P و رئوسی از بخش Y را که عضو حداقل یکی از این مسیرها هستند را Q می نامیم. حال مجموعه P+(Y-Q) یکی مجموعه مستقل با اندازه خواسته شده است ( تمرین 5 ).
کاربرد
الگوریتم پیدا کردن تطابق ماکسیمم در حل بسیاری از مسائل می تواند تأثیرگذار باشد. مثلاً می‌توان بزرگترین مجموعه‌ی مستقل (مجموعه‌ی از رئوس که هیچ دوتایی به یکدیگر یال ندارند) در گراف دوبخشی را نیز پیدا کرد یا کوچکترین پوشش رأسی (مجموعه ی از راس ها که به ازای هر یال یک سر آن در این مجموعه باشد)؛ (حل این مسائل به خواننده توصیه می شود). پیدا کردن راه حلی برای این مسائل باعث حل مسائل زیاد دیگری در نظریه گراف شد که تأثیر بسزایی در حل مسائل الگوریتمی دارد. پس داشتن یک برنامه مناسب و دقیق برای پیدا کردن تطابق می تواند بسیار مفید باشد.
تمرین
  1. ثابت کنید در گراف G با n راس که دارای هیچ راس تنهایی نمی باشد اندازه تطابق ماکسیمم بعلاوه اندازه پوشش یالی مینیمم برابر n است.
  2. ثابت کنید در هر گراف دوبخشی G اندازه بزرگترین تطابق برابر اندازه کوچکترین پوشش راسی می باشد.
  3. ثابت کنید در هر گراف دوبخشی G اندازه بزرگترین مجموعه مستقل برابر اندازه کوچکترین پوشش یالی می باشد.
  4. ثابت کنید اندازه کوچکترین پوشش راسی بعلاوه اندازه کوچکترین پوشش یالی برابر n می باشد.
  5. ثابت کنید اندازه بزرگترین مجموعه مستقل در گراف دوبخشی برابر n ( تعداد رئوس گراف ) منها اندازه تطابق ماکسیمم در گراف می باشد.
  6. ثابت کنید روشی که برای پیدا کردن بزرگترین مجموعه مستقل در گراف دوبخشی داده شده است این مجموعه را پیدا می کند.
  7. ( مسئله ازدواج پایدار ) فرض کنید n مرد و n زن می خواهند ازدواج کنند به صورتی که هر مرد لیستی اولویت بندی شده از زن هایی دارد که می خواهد با آنها ازدواج کند ( می توانید فرض کنید هر مرد به زن ها اعداد 1 تا n را که نشانه اولویت آنهاست نسبت می دهد ). همچنین زن ها نیز لیسیت از اولویت های خود مانند زن ها دارند. حال می خواهیم ترتیبی از ازدواج را بین این n مرد و n زن برقرار کنیم به طوری که هیچ زن و مردی نباشند که همدیگر را به همسرهای کنونی خود ترجیح دهند. الگوریتمی برای انجام این کار بدست آورید.
منبع
برای مطالعه بیشتر در این زمینه می توانید به فصل سوم کتاب آشنایی با نظریه گراف نوشته وست مراجعه کنید.
 
 
Top پرسش و پاسخ:
•  در این بخش پاسخ پرسش‌های پرسیده‌شده نگاشته می‌شود.
  • پرسش: نتایج مرحله‌ی اوّل شانزدهمین دوره (سال ۱۳۸۵) چه زمانی و چگونه اعلام می‌شود؟
  • پاسخ: نیمه‌ی اسفندماه و از طریق سایت باشگاه و احتمالاً یکی از روزنامه‌های کثیرالانتشار.
  • پرسش: حداقل امتیاز لازم برای قبولی در مرحله‌ی اوّل چند نمره است؟
  • پاسخ: بسته به عمل‌کرد سایر شرکت‌کنندگان و درجه‌ی سختی سؤالات این حدنصاب متغیر بوده و میزان از پیش تعیین شده‌ای نمی‌باشد. با این حال، پیش‌بینی می‌شود این میزان کمتر از نصف حداکثر امتیاز قابل اکتساب باشد.
  • پرسش: در مورد دوره‌ی نوروزی: لطفاً مسائی واکنشی (دسته‌ی دوّم) را بیشتر توضیح دهید و اگر ممکن است یک مثال بزنید.
  • پاسخ: به عنوان مثال این سوال را که در المپیاد جهانی ۲۰۰۰ مطرح شده است، در نظر بگیرید: به شما یک فایل از نوع median.o و یک فایل از نوع median.h داده می‌شود که این توابع را دارند:
    int get_n();
    int median(int i, int j, int k);
    
    void report(int m);
    
    مسئله به این صورت است که ما یک آرایه‌ی حداکثر ۱۵۰۰ عضوی داریم که شما باید اندیس عنصر میانه‌ی آن را پیدا کنید. برای این کار اگر تابع اول را صدا کنید اندازه‌ی آرایه را دریافت خواهید کرد؛ با اجرای تابع دوم اندیس عنصر میانه ی سه عنصری که اندیس آن را به تابع داده اید را دریافت خواهید کرد (یکی از سه پارامتر تابع)؛ و با اجرای تابع سوم اندیس جواب را اعلام خواهید کرد و برنامه ی شما متوقف خواهد شد. بدیهی است که اجرای هر یک از سه تابع با پارامترهای بی معنا باعث گرفتن نمره‌ی ۰ از تست مورد نظر خواهد شد، در ضمن تعداد اجراهای تابع دوم نباید از ۷۰۰ بار بیشتر باشد.
    شما باید با فرض این‌که این توابع در median.o (که یک object file است)، پیاده‌سازی شده‌اند (و البته کد آن‌ها در دسترس نیست)، فایل median.h را (که یک header file است) در ابتدای برنامه‌ی خودتان include کرده و به هنگام اجرا نیز با لینک‌کردن median.o در دستور کامپایل، برنامه‌ی خود را تست کنید.


Copyright © 2005, 2006. All rights reserved for committee of olympiad in informatics.
Designed by Aideen NasiriShargh