26.2-9 Antag att rätt lösning är att dela upp V i V 1 och V 2 (V 1 V 2 =, V 1 V 2 = V ). Antal kanter vi måste skära är då det minsta snittet mellan v 1 och v 2, där v 1 är ett godtyckligt hörn i V 1 och v 2 är ett godtyckligt hörn i V 2. Om vi kände till denna uppdelning skulle vi kunna hitta lösningen på följande sätt: Vi bygger upp en flödesgraf G vars hörnmängd sammanfaller med G:s, och där varje kant (i, j) ersätts med två kanter (i, j) och (j, i), båda med kapacitet 1. Vi ser nu att ett snitt i grafen G motsvarar en uppdelning av grafen G. Eftersom vi vet att min-snitt är lika med max-flöde använder vi en maxflödes-algoritm där vi låter v 1 vara källa och v 2 vara sänka. I det givna problemet känner vi inte rätt uppdelning V 1 och V 2, och kan därför inte välja hörnen v 1 och v 2. Vi använder därför en form av uttömmande sökning. Vi väljer godtyckligt v 1 V som källa. Vi gör därefter V 1 körningar där vi låter sänkan v 2 V \ {v 1 } löpa över hela hörnmängden. I varje iteration kommer vi då göra en maxflödes-algoritm på en graf med V hörn och 2 E kanter. Algoritmen returnerar det minsta maxflödet som svar. Återstår att visa att denna algoritm returnerar rätt svar. Enligt det inledande resonamanget får vi rätt svar om v 1 V 1 och v 2 V 2. I alla övriga fall måste vi få ett högre resultat. Av symmetriskäl kan vi alltid anta vi placerat v 1 rätt. Då måste även v 2 i någon iteration placeras i V 2, och denna iteration kommer ge rätt svar. Övriga iterationer kommer att ge ett högre resultat. 26-2 a. Vi kan notera att en stig har egenskapen att varje hörn kan ha högst en ingående och en utgående kant. Eftersom stigarna i P måste vara disjunkta gäller detta även för varje hörn i P. Vi bygger upp hjälpgrafen G = (V, E ) på följande sätt: För varje hörn i V skapar vi två hörn x i och y i. Vi låter sedan en kant gå mellan x i och y j då det finns en kant (i, j) E. Vi kan nu göra iakttagelsen att en matchning M i G kan översättas till en stigtäckning i G av följande skäl. I en matchning ligger varje hörn i högst en kant. I en stig ligger varje hörn i högst två kanter. 1
Vi gör överföringen från matchning till stigtäckning så att kanten (i, j) ingår i någon stig om kanten (x i, y j ) ingår i matchningen. Eftersom det högst kan finnas en sådan kant i matchningen kan i högst ha en utgående kant och j högst en ingående kant. Eftersom detta gäller alla hörn bildar denna överföring en stigtäckning. Återstår att forma om denna kantmängd till en stigmängd P, där varje element p P är en sammanhängande graf med m hörn och m 1 kanter (dvs en stig). Vi utelämnar detaljerna men konstanterar att en sådan överföring kan göras i tid O( V ). Vi känner en metod att beräkna en maximal matchning M genom att lägga till hjälphörn x 0 och y 0 så att det går en kant från x 0 till varje x i och en kant från varje y i till y 0. Det återstår att visa att denna maximala matchning faktiskt motsvarar en minimal stigtäckning. För p P låter vi v(p) vara antal hörn och e(p) vara antal kanter i p. Vi konstaterar att V = p P v(p) = p P (1 + e(p)) = p P 1 + p P e(p). Eftersom varje kant i stigtäckningen motsvaras av en kant i matchningen gäller p P e(p) = M. Dessutom gäller enligt lemma att M = f, där f är flödet som motsvarar den bipartita matchningen M. Alltså har vi V = P + f. Det betyder att stigtäckning har sitt minimum när flödet har sitt maximum. Algoritmen för att hitta den minimala stigtäckningen blir då som följer: 1. Skapa den bipartita hjälpgrafen G. 2. Hitta en maximal bipartit matchning i G genom att lägga till hjälphörn och beräkna maximalt flöde. 3. Skapa stigtäckningen utifrån matchningen såsom beskrivet. Tidskomplexiteten för de olika stegen är 1. O(V + E) 2. O(V E) 3. O(E) 2
b. - 8.2-4 vilket ger en total komplexitet på O(V E). Vi låter a i beteckna de n talen. Förbehandlingssteget i vår algoritm ser ut så här: 1. För i = 1,..., k sätt c[i] = 0. 2. För i = 1,..., n sätt c[a i ] = c[a i ] + 1. 3. För i = 0,..., k sätt c[i] = c[i 1] + c[i]. Nu innehåller c[i] antal tal upp till och med i. För att svara på frågan hur många tal som ligger mellan a och b beräknar vi c[b] c[a 1]. Tid för förbehandlingen blir Θ(k) + Θ(n) + Θ(k) = Θ(n + k). För att svara på frågan gör vi bara en beräkning, som alltså går i konstant tid (om vi antar enhetskostnad). 8.3-4 Vi använder idén från radixsortering. Låt a 1, a 2,..., a n vara de n talen. Vi beräknar b i och c i så att a i = b i + nc i. Talen b i och c i kommer att ligga i intervallet [0,..., n 1]. Vi gör nu räknesorting av tuplerna (a i, b i, c i ) med avseende på b i. Därefter gör vi räknesortering en gång till med avseende på c i. Enligt resonemanget för att radixsort fungerar ger även detta en korrekt sortering. Tiden för beräkning av b i och c i är O(n) (med enhetskostnad) och de två sorteringsstegen tar vart och ett O(n). Totalt ger detta en tidskomplexitet på O(n). 8-5 a. En 1-sorterad lista är en lista som är sorterad i den vanliga bemärkelsen. b. Exempelvis 1, 3, 2, 4, 5, 6, 7, 8, 9, 10. (Att denna lista är 2-sorterad kan lätt verifieras.) 3
c. Definitionen av att en lista är k-sorterad är i+k 1 j=i A[j] i+k j=i+1 A[j] k k Vi multiplicerar båda leden med k: i+k 1 j=i A[j] i+k j=i+1 Vi subtraherar i+k 1 j=i+1 A[j]: A[i] A[i + k] A[j],. vilket är den olikhet vi skulle visa. d. Låt oss anta att listans längd n är en multipel av k, säg n = mk. (Resonemanget nedan fungerar även om detta inte är fallet, men det tillkommer en del specialfall att ta hänsyn till.) Idén är att sortera dellistorna (A[1], A[k + 1], A[2k + 1],..., A[(m 1)k + 1]), (A[2], A[k + 2], A[2k +2],..., A[(m 1)k +2]), (A[3], A[k +3], A[2k +3],..., A[(m 1)k +3]) osv var och en för sig. Mer precist, låt listan A i = (A[i], A[k + i], A[2k + i],..., A[(m 1)k + i) för i = 1,..., k. Vi använder en O(n log(n))-algoritm (exempelvis mergesort) för att sortera de olika listorna A 1, A 2,..., A k. När detta är klart gäller uppenbart olikheten från föregående steg, och listan är k-sorterad. Längden hos varje lista är n/k. Att sortera en lista tar tid O(n/k log(n/k)) och att sortera samtliga k listor tar tid ko(n/k log(n/k)) = O(n log(n/k)). e. Vi antar att k är en 2-potens, k = 2 t. (Det går att modifiera algoritmen för godtyckligt k, men det tillkommer en del specialfall.) Vi har k listor A 1, A 2,..., A k av längd n/k som var och en är sorterad. Idén är nu att iterativt slå ihop två listor tills vi har en lista av längd n. Algoritmen blir som följer: 1. Låt i = 1. 4
2. För j från 0 till k/ ( 2 i) 1 sortera ihop listorna A j2 i +1 och A (j+1)2 i +1. Lagra resultatet i A j2 i +1. 3. Låt i = i + 1. Om i < t så upprepa föregående steg, annars mata ut A 1. I varje iteration behandlas varje element i listan en gång, och vi har log 2 k iterationer, så den totala tidskomplexiteten blir O(n log(k)). f. Vi vill visa att det inte går att k-sortera en lista snabbare än Ω(n log(n)) om k antas vara en konstant. Vi känner till att en k-sorterad lista kan sorteras fullständigt i tid O(n log(k)). Antar vi att k är konstant är detta samma sak som O(n). Antag nu att det går att k-sortera i tid o(n log(n)). Vi skulle då kunna först k-sortera en godtycklig lista och sedan använda algoritmen för att sortera den fullständigt. Total tid för detta vore o(n log(n)) + O(n) = o(n log(n)). Vi vet dock att jämförelsebaserad sortering alltid tar tid Ω(n log(n)), varför k-sortering i tid o(n log(n)) ej kan vara möjlig. 5