Asosiy mazmunga o'tish

30-dars: DSU

4-darajaPlatina — dinamik programmalash25–32 darslar

Bu darsdan keyin siz

  • DSU ning ikki amalini yozasiz: topish va birlashtirish
  • yo’lni siqish va o’lchamga qarab ulash nima berishini tushuntirasiz
  • DSU qaysi masalalarda kerakligini taniysiz

Avval qo‘lda

~7 daqiqaguruh bilan6 ta qog'oz varaqcha va ip (yoki chiziq)

Oltita varaqchaga 1 dan 6 gacha raqam yozing va stolga qo’ying. Har varaqchadan o’ziga strelka chizing — hozir har kim o’z guruhining vakili.

Endi «1 va 2 birlashdi» deyilsa, birining strelkasini ikkinchisiga qarating. «Kimning guruhidasan?» degan savolga strelkalar bo’ylab yurib javob bering.

Bir necha birlashtirishdan keyin strelkalar zanjiri uzayib ketadi. Endi qoidani o’zgartiring: javob topilgach, yo’lda uchragan hamma varaqchani to’g’ridan-to’g’ri vakilga qarating.

Nimani sezishingiz kerak

Ikkinchi qoida bilan zanjir uzayib ketmaydi: har so’rov uni kalta qilib qo’yadi. Bu — yo’lni siqish.

Kompyuter yopiq. Bu mashq tugamaguncha kod ochilmaydi.

DSUdisjoint set union

Bir-biri bilan kesishmaydigan guruhlarni saqlaydigan tuzilma. Ikki amali bor: element qaysi guruhda ekanini topish va ikki guruhni birlashtirish.

Har guruhning bitta vakili bo’ladi va har element vakiliga ko’rsatkich saqlaydi. Vakil o’zini ko’rsatadi.

eng sodda ko'rinish
n = 6ota = list(range(n + 1))def topish(x):  while ota[x] != x:      x = ota[x]  return xota[2] = 1ota[3] = 2print(topish(3), topish(1), topish(5))

Avval o'ylang: bu kod nima chiqaradi? Ishga tushirishdan oldin yozing.

Bashorat qiling
n = 6ota = list(range(n + 1))def topish(x):  while ota[x] != x:      x = ota[x]  return xota[2] = 1ota[3] = 2print(topish(3), topish(1), topish(5))
Javobni ko'rish
1 1 5

3 dan 2 ga, 2 dan 1 ga — zanjir bo'ylab yurildi va vakil 1 topildi. 5 esa hali yolg'iz, shuning uchun o'zini ko'rsatadi.

Bu ko’rinish ishlaydi, lekin zanjir uzayib ketishi mumkin: n ta birlashtirish uzunligi n bo’lgan zanjir hosil qilsa, har so’rov O(n) turadi.

Masala: har birlashtirishdan keyin nechta guruh qolganini chiqaring. Birinchi fikr — har safar grafni boshidan kezib sanash.

sekin.py
n = 6amallar = [(1, 2), (3, 4), (2, 3), (1, 4)]qoshni = [[] for _ in range(n + 1)]javoblar = []for a, b in amallar:  qoshni[a].append(b)  qoshni[b].append(a)  korilgan = [False] * (n + 1)  guruhlar = 0  for boshi in range(1, n + 1):      if korilgan[boshi]:          continue      guruhlar += 1      korilgan[boshi] = True      stek = [boshi]      while stek:          u = stek.pop()          for v in qoshni[u]:              if not korilgan[v]:                  korilgan[v] = True                  stek.append(v)  javoblar.append(guruhlar)print(javoblar)

Avval o'ylang: bu kod nima chiqaradi? Ishga tushirishdan oldin yozing.

Bashorat qiling
n = 6amallar = [(1, 2), (3, 4), (2, 3), (1, 4)]qoshni = [[] for _ in range(n + 1)]javoblar = []for a, b in amallar:  qoshni[a].append(b)  qoshni[b].append(a)  korilgan = [False] * (n + 1)  guruhlar = 0  for boshi in range(1, n + 1):      if korilgan[boshi]:          continue      guruhlar += 1      korilgan[boshi] = True      stek = [boshi]      while stek:          u = stek.pop()          for v in qoshni[u]:              if not korilgan[v]:                  korilgan[v] = True                  stek.append(v)  javoblar.append(guruhlar)print(javoblar)
Javobni ko'rish
[5, 4, 3, 3]

Oxirgi amalda 1 va 4 allaqachon bitta guruhda edi, shuning uchun son o'zgarmadi. Yechim to'g'ri, lekin har amalda butun graf qaytadan kezildi.

Har so’rovda butun graf kezib chiqiladi: q × (n + q) amal. Chegara esa n ≤ 10⁵, q ≤ 2 · 10⁵.

Sig‘adimi?

Bir soniyada taxminan 10⁸ oddiy amal bajariladi.

Yechimn = 1 000n = 200 000
Har amalda qayta kezishn^210⁶sig‘adi10¹⁰sig‘maydi
DSUn1 000sig‘adi200 000sig‘adi

Yashil — yechim o‘tadi. Sariq — chegarada, konstanta va til muhim bo‘lib qoladi. Qizil — bu yechim bilan bormaydi, boshqasini qidiring.

Ikkita hiyla zanjirni kalta saqlaydi. Birinchisi — yo’lni siqish: vakil topilgach, yo’lda uchragan hamma element to’g’ridan-to’g’ri vakilga ulanadi. Ikkinchisi — o’lchamga qarab ulash: kichik guruh kattasiga ulanadi, teskarisi emas.

tez.py
n = 6ota = list(range(n + 1))olcham = [1] * (n + 1)def topish(x):  ildiz = x  while ota[ildiz] != ildiz:      ildiz = ota[ildiz]  while ota[x] != ildiz:      ota[x], x = ildiz, ota[x]  return ildizdef birlashtir(a, b):  ra, rb = topish(a), topish(b)  if ra == rb:      return False  if olcham[ra] < olcham[rb]:      ra, rb = rb, ra  ota[rb] = ra  olcham[ra] += olcham[rb]  return Trueguruhlar = njavoblar = []for a, b in [(1, 2), (3, 4), (2, 3), (1, 4)]:  if birlashtir(a, b):      guruhlar -= 1  javoblar.append(guruhlar)print(javoblar)

Avval o'ylang: bu kod nima chiqaradi? Ishga tushirishdan oldin yozing.

Bashorat qiling
n = 6ota = list(range(n + 1))olcham = [1] * (n + 1)def topish(x):  ildiz = x  while ota[ildiz] != ildiz:      ildiz = ota[ildiz]  while ota[x] != ildiz:      ota[x], x = ildiz, ota[x]  return ildizdef birlashtir(a, b):  ra, rb = topish(a), topish(b)  if ra == rb:      return False  if olcham[ra] < olcham[rb]:      ra, rb = rb, ra  ota[rb] = ra  olcham[ra] += olcham[rb]  return Trueguruhlar = njavoblar = []for a, b in [(1, 2), (3, 4), (2, 3), (1, 4)]:  if birlashtir(a, b):      guruhlar -= 1  javoblar.append(guruhlar)print(javoblar)
Javobni ko'rish
[5, 4, 3, 3]

Javob bir xil. birlashtir allaqachon bir guruhda bo'lganlar uchun False qaytaradi — shuning uchun hisoblagich noto'g'ri kamaymaydi.

DSU bitta savolga javob beradi: «bu ikkisi bir guruhdami?» — va guruhlar faqat qo’shiladi, hech qachon bo’linmaydi.

Shu ikki shart bajarilsa DSU ishlaydi: bog’langan guruhlar sonini kuzatish, halqa borligini aniqlash, MST qurish (31-dars). Guruhni ajratish kerak bo’lsa — DSU yaramaydi.

Vizualda uch ustidagi son — uning guruhi vakili. Bir guruhdagilar bir xil sonni ko’rsatishini kuzating.

DSU — birlashtirish va topish

Uch ustidagi son — guruh vakili. Bir guruhdagilar bir xil sonni ko'rsatadi.

Har guruhning vakili bor; birlashtirishda kichik guruh kattasiga ulanadi.

Algoritmning har qadami quyidagi ro‘yxatda matn sifatida ham berilgan — vizual faqat shu qadamlarni chizadi, yangi ma’lumot qo‘shmaydi.

  1. Boshida har kim o'z guruhida yolg'iz: 6 ta guruh. Uch ustidagi son — uning guruhi vakili.
  2. 1 va 2 birlashdi. Kichik guruh kattasiga ulandi, yangi vakil — 1. Guruhlar soni: 5.
  3. 4 va 5 birlashdi. Kichik guruh kattasiga ulandi, yangi vakil — 4. Guruhlar soni: 4.
  4. 2 va 3 birlashdi. Kichik guruh kattasiga ulandi, yangi vakil — 1. Guruhlar soni: 3.
  5. 5 va 6 birlashdi. Kichik guruh kattasiga ulandi, yangi vakil — 4. Guruhlar soni: 2.
  6. 3 va 4 birlashdi. Kichik guruh kattasiga ulandi, yangi vakil — 1. Guruhlar soni: 1.
  7. 1 va 6 allaqachon bitta guruhda (vakili 1). Hech narsa o'zgarmaydi — bu qirra halqa hosil qilgan bo'lardi.
  8. Hammasi bitta guruhda. Kichikni kattaga ulash qoidasi zanjirning uzayib ketishiga yo'l qo'ymaydi — shu sababli «vakiling kim?» degan savol deyarli bir qadamda javob oladi.
123654

Boshida har kim o'z guruhida yolg'iz: 6 ta guruh. Uch ustidagi son — uning guruhi vakili.

Amal0

1/8

Tuzoq — xatoni toping

30 / 40

MasalaHar birlashtirishdan keyin guruhlar sonini chiqaring.

Taklif qilingan yechim

Bu javobning 3-qismi noto'g'ri. Ikki xato bir qatorda. Birinchisi: ota[b] = a — VAKIL emas, elementning o'zi ulanmoqda; bu bor bog'lanishlarni buzadi. Ikkinchisi: ular allaqachon bir guruhda bo'lishi tekshirilmayapti, shuning uchun hisoblagich ortiqcha kamayadi. To'g'risi: ota[rb] = ra va faqat ra != rb bo'lganda.

Javobning qaysi qismi noto'g'ri? Bosib belgilang.

1DSU ning ikki amali qanday?

Javobni ko'rish

topish(x) — x qaysi guruhda ekanini bilish, birlashtir(a, b) — ikki guruhni qo’shish.

2Yo'lni siqish nima qiladi?

Javobni ko'rish

Vakil topilgach, yo’lda uchragan hamma elementni to’g’ridan-to’g’ri vakilga ulaydi. Shuning uchun keyingi so’rovlar deyarli bir qadamda javob beradi.

3Nega kichik guruh kattasiga ulanadi?

Javobni ko'rish

Aks holda zanjir uzayib ketadi: har birlashtirishda chuqurlik bittaga oshaveradi. Kichikni kattaga ulash chuqurlikni logarifmik ushlab turadi.

4DSU qanday masalada YARAMAYDI?

Javobni ko'rish

Guruhlarni ajratish kerak bo’lganda. DSU faqat qo’shishni biladi: bir marta birlashgan guruhlarni qaytarib ajratib bo’lmaydi.

5birlashtir nega False qaytaradi?

Javobni ko'rish

Ular allaqachon bitta guruhda bo’lganini bildirish uchun. Shu javobga qarab guruhlar hisoblagichi kamaytiriladi yoki qoldiriladi.

Masalalar

3 ta

  • Road ConstructionCSES 1676o'rta

    Darsdagi masalaning o‘zi, ustiga eng katta guruh o‘lchami ham so‘ralgan.

  • Road ReparationCSES 1675o'rta

    Bu allaqachon MST — 31-darsning masalasi. DSU tayyor bo‘lgach u bir necha qatorga sig‘adi.

  • Cut RibbonCodeforces 189Aoson

    Bu DSU emas, 1D DP (26-dars) — usulni chalkashtirmaslikni ham mashq qilish kerak.

Lokal mashq: mashqlar/30-guruhlar/ — masala matni, testlar va tekshir.py. Avval sekin.py ni o‘zingiz yozing, keyin tayyorini oching.

Topshiriq — darajangizni tanlang

Asos · 5–7-sinf — Vizual va aniq ko'rsatmali

Oltita varaqcha bilan mashqni takrorlang, lekin bu safar o’lchamga qarab ulashni qo’llang: har safar kichik guruhni kattasiga ulang. Zanjirning eng katta uzunligi qanchaga yetdi?

Mustahkam · 8–9-sinf — Matnli masala, o'zingiz tuzasiz

30-guruhlar masalasini yeching. Takroriy birlashtirish bo’lgan testni ataylab yasang va yechimingiz to’g’ri javob berishini tekshiring.

Chuqur · 10–11-sinf — Algoritmik, chegara holatlari bilan

Ikki hiylani alohida o’chirib ko’ring: faqat yo’lni siqish, faqat o’lchamga qarab ulash, ikkalasi ham yo’q. Uch variantni 200 ming amalda o’lchang va vaqtlarni jadvalga yozing.

Darsni belgilash uchun JavaScript kerak. Bu progressni saqlash uchun ishlatiladi — darslikning o'zi JavaScript'siz ham to'liq o'qiladi.