Kommentarer och lösningar
till uppgifterna i
Kvalet till Programmeringsolympiaden 2002.
Kom ihåg
att alla sätt att lösa en uppgift är rätt utom de som
är fel.
Lösningarna är skrivna i C/C++ och följer tävlingsstandarden
på så sätt att de är helt okommenterade och obegripliga.
Om någon VB/Pascalprogrammerare vill dela med sig av sina lösningar
mottages dessa tacksamt.
Uppgifterna (PDF)
Testdata (PDF)
Uppgift 1 - Euro
Eftersom avgiften utgår
om man växlar in och ut på samma bank, blir det lite krångligt
att titta på växlingarna separat. Det enklaste är istället
att göra en dubbel-loop: för varje kombination av ut- och inväxlingsbank
(max 5*5 st) beräknas den totala kostnaden för resan. Den minimala
kostnaden och de banker som hör ihop med denna kostnad sparas under
tiden.
Lösningsförslag
Uppgift 2 - Primfaktorer
Att gå igenom serien
kan göras antingen i en loop eller rekursivt som i lösningen nedan.
För att hitta primfaktorerna till ett tal N behövs ingen smart
algoritm eftersom talen är så små. Exempelvis kan man testa
alla tal K med början från 2 och kolla om N är jämnt
delbart med K. I så fall är K en primfaktor eftersom det inte
fanns något lägre tal som N var delbart med. När man hittat
en primfaktor K så dividerar man N med K och fortsätter leta där
man slutade. Man måste naturligtvis tänka på att samma faktor
kan förekomma flera gånger.
Lösningsförslag
Uppgift 3 - Kedjebrev
Håll reda på
antal personer c som har fått brev i senaste omgången, från
början är c = m. I varje omgång kan man då förvänta
sig att den person som står på tur att få pengar ska få
c kronor, därför blir antalet avhoppare i den omgången skillnaden
mellan c och det verkliga beloppet. Nu vet vi att antalet personer som fortfarande
är med är lika med det verkliga beloppet och genom att multiplicera
med m så får vi c för nästa omgång.
Lösningsförslag
Uppgift 4 - Tomter
till husen
Här finns två
(minst) olika metoder. Den mest effektiva är att för varje längd
på tomten hitta den minimala och maximala bredden och sedan låta
bredden anta alla värden däremellan och beräkna arean. Den
minimala bredden erhålls genom att trycka ihop modulerna så mycket
som möjligt, den maximala genom att dra en lång "utstickare".
Det är lätt att kontrollera på papper att det inte finns
någon "glesare" placering av modulerna eftersom de måste ha en
kant gemensam. En sämre lösning (som ändå fungerar eftersom
det maximala antalet moduler är så litet) är att generera
alla möjliga husformer med "brute force". Detta kan göras rekursivt.
Man börjar med två moduler och försöker sedan utöka
med en modul i alla tänkbara riktningar. När man har satt dit denna
försöker man sätta nästa i alla möjliga riktningar.
Hela tiden håller man reda på maximalt/minimalt x och y så
att man när den sista modulen sätts på plats lätt kan
räkna ut arean.
För att undvika dubletter
i båda dessa metoder är det enklast att ha en lista över
areor (t.ex. 1 till 300 så får man med alla med god marginal).
Från början är ingen av dessa areor markerad men så
fort man hittar en husform markerar man dess area som möjlig i listan.
Efteråt går man igenom listan och räknar hur många
areor som har markerats.
Lösningsförslag
Uppgift 5 - Priser
med mynt
Även här finns
flera olika möjligheter. Först gäller det att inse att talen
inte kan ha mer än 3 siffror (eftersom 4*12<1000/20). Därför
finns det högst 3*12 mynt totalt. Ett enkelt sätt är då
att testa alla möjliga kombinationer av antal femtioöringar/antal
enkronor/antal extramynt (högst 19*37*37 stycken, jämnt antal femtioöringar)
med hjälp av en trippel-loop. För varje kombination beräknar
man myntens värde och därefter hur många mynt som skulle
krävas för att skriva detta belopp. Om det antalet råkar
överensstämma med det totala antalet mynt i kombinationen markerar
man beloppet som möjligt på samma sätt som i uppgift 4. Efteråt
är det bara att räkna antal markerade belopp.
Lösningsförslag
Uppgift 6 - Avstånd
mellan tal
Detta är en typisk
backtracking-uppgift (rekursiv). Man börjar att skjuta den första
pilen där det finns ett r. Man markerar den ringen som upptagen, t.ex.
genom att spara numret på pilen där istället för r:et.
Sedan försöker man skjuta nästa pil på de återstående
r:en i tur och ordning. När man hittat en ring som det är tillåtet
att skjuta på markerar man denna och fortsätter skjuta nästa
pil genom att låta funktionen anropa sig själv. Om man inte hittar
något godkänt r, är det bara att låta funktionen avslutas
för då fortsätter man att leta ringar för den föregående
pilen. Man måste då naturligtvis göra den aktuella ringen
ledig genom att sätta den till r igen. När man har lyckats skjuta
lika många pilar som det finns ringar har man hittat en lösning,
då är det bara att skriva ut ordningen och avsluta. För att
se till att avståndet hela tiden växer, kan man ha en extra parameter
till den rekursiva funktionen som beskriver det minsta tillåtna avståndet
till nästa ring. Vid rekursionen sätter man denna parameter till
avståndet mellan de senaste två ringarna.
Lösningsförslag