Luonnollisen luvun n kertoma lasketaan kaavalla 1 · 2 · 3 · ... · n. Esimerkiksi luvun 5 kertoma on 1 · 2 · 3 · 4 · 5 = 120. Lisäksi on sovittu, että luvun 0 kertoma on 1. Ratkaisun tulee toimia nopeasti myös isoilla syötteillä.
Tee ohjelma Kertoma.java
, joka kysyy käyttäjältä luvun ja tulostaa sitten sen kertoman. Voit olettaa, että tuloksena oleva kertoma mahtuu int
-tyyppiseen kokonaislukumuuttujaan.
Anna luku: 5 Kertoma: 120
Esimerkissä punaisella väritetyt kohdat vastaavat käyttäjän kirjoittamaa tekstiä. Toteuta ohjelma tarkalleen esimerkin mukaisesti, koska ohjelma tarkistetaan automaattisesti.
HUOMMikäli testien suorituksessa on ongelmia, tarkistathan että sinulla on uusin TMC plugin asennettuna. Voit tarkastaa sen NetBeanssissa seuraavasti Help - Check for updates. Mikäli päivityksiä on saatavilla, asennathan ne.
Javan tavalliset kokonaislukumuuttujat eivät voi sisältää kovin suuria lukuja. Esimerkiksi int
-tyyppisen muuttujan suurin sallittu arvo on 2147483647 (reilut kaksi miljardia). Jos ohjelmassa on tarpeen käsitellä suuria kokonaislukuja, apuun tulee luokka BigInteger
. Ratkaisun tulee toimia nopeasti myös isoilla syötteillä.
Tee ohjelma SuuriKertoma.java
, joka toimii edellisen ohjelman tavoin, mutta se laskee tarvittaessa suuriakin kertomia.
Anna luku: 25 Kertoma: 15511210043330985984000000
Löydät tietoa luokasta BigInteger
Googlella: hakusanat "java biginteger api" vievät Javan dokumentaatioon ja hakusanat "java biginteger example" antavat esimerkkejä. Samalla tavalla voi etsiä tietoa mistä tahansa Javan luokasta.
Palindromi on sana, joka on sama alusta loppuun ja lopusta alkuun luettuna. Esimerkiksi sanat ala
ja enne
ovat palindromeja.
Tee ohjelma Palindromi.java
, joka lukee käyttäjältä sanan ja ilmoittaa, onko se palindromi. Voit olettaa, että sana muodostuu kirjaimista a
–z
.
Anna sana: enne Sana on palindromi.
Anna sana: aika Sana ei ole palindromi.
Tee ohjelma PieninSuurin.java
, joka lukee käyttäjältä joukon kokonaislukuja ja tulostaa lopuksi pienimmän ja suurimman luvun. Jokainen kokonaisluku mahtuu int
-muuttujaan. Ohjelma kysyy ensin, kuinka monta lukua käyttäjä antaa. Voit olettaa, että käyttäjä antaa ainakin yhden ja korkeintaan miljoona lukua.
Kuinka monta? 5 Anna luvut: -5 2 -1 7 3 Pienin: -5 Suurin: 7
Tee ohjelma Kirjainnelio.java
, joka tulostaa annetun kokoisen kirjainneliön seuraavien esimerkkien mukaisesti. Voit olettaa, että kirjainneliön koko (kerrosten määrä) on 1–26.
Anna koko: 3 AAAAA ABBBA ABCBA ABBBA AAAAA
Anna koko: 4 AAAAAAA ABBBBBA ABCCCBA ABCDCBA ABCCCBA ABBBBBA AAAAAAA
Tee ohjelma Anagrammit.java
, joka tarkistaa, ovatko kaksi sanaa anagrammeja. Sanat ovat anagrammeja, jos jokainen kirjain esiintyy niissä yhtä monta kertaa. Esimerkiksi sanat talo
ja lato
ovat anagrammeja. Voit olettaa, että sanat muodostuvat kirjaimista a
–z
.
Anna 1. sana: talo Anna 2. sana: lato Annoit anagrammit.
Anna 1. sana: talo Anna 2. sana: altto Et antanut anagrammeja.
Tehtävänäsi on etsiä annetusta lukujonosta sellainen alkuosa jonka summa on haluttu. Ohjelmalle annetaan lukujono jonka jälkeen suoritetaan monta kyselyä.
Etsin
jonka konstruktorille Etsin(int[] luvut)
annetaan taulukollinen positiivisia kokonaislukujaEtsin.etsi(int haluttuSumma)
joka kutsulla etsi(x)
palauttaa indeksin i
konstruktorille annettuun taulukkoon luvut
siten, että luvut[0] + luvut[1] + ... + luvut[i] = x
Palauta -1
jos tällaista indeksiä ei löydy.Tee ohjelmastasi salamannopea. Testit testaavat että 10 000 kyselyä 100 000 luvun taulukosta vie alle 50ms.
Huom! Testit eivät testaa pääohjelmaa (main
) vaan suoraan luokkaa Etsin
!
Vihje! binäärihaku
Montako lukua? 3 Anna luvut: 1 2 3 Mikä summa? 1 Indeksi: 0 Mikä summa? 3 Indeksi: 1 Mikä summa? 6 Indeksi: 2 Mikä summa? 5 Indeksi: -1
Montako lukua? 10 Anna luvut: 10 20 4 7 7 5 7 7 10 1 Mikä summa? 48 Indeksi: 4
Fibonacci.java
,
joka laskee luvun F(n). Voit olettaa,
että n on ainakin 0 ja korkeintaan 46,
jolloin tulos mahtuu int
-muuttujaan.
Tee ohjelmasta niin tehokas, että se laskee
luvun F(46) salamannopeasti.
n: 5 F(n): 5
n: 9 F(n): 34
Tee ohjelma PuuttuvaLuku.java
, joka toimii seuraavasti: Käyttäjä antaa ensin ohjelmalle kokonaisluvun n ja sitten kaikki kokonaisluvut väliltä 1...n yhtä lukuun ottamatta. Ohjelman tehtävänä on selvittää, mikä on puuttuva luku. Voit olettaa, että käyttäjä antaa ainakin yhden luvun ja korkeintaan miljoona lukua. Tee ohjelmasta niin tehokas, että se käsittelee miljoona lukua muutamassa sekunnissa. Testit testaavat ajankäytön.
Suurin luku? 5 Anna luvut: 3 1 5 4 Puuttuva luku: 2
Suurin luku? 5 Anna luvut: 3 1 2 4 Puuttuva luku: 5
Vihje: Tehtävään on olemassa ratkaisualgoritmi, jonka aikavaativuus on O(n) ja tilavaativuus on O(1).
Tee ohjelma SuurinSumma.java
, joka selvittää suurimman peräkkäisten lukujen summan käyttäjän antamissa luvuissa. Ohjelma kysyy ensin, kuinka monta lukua käyttäjä antaa.
Tässä tehtävässä summaan täytyy kuulua ainakin yksi luku. Voit olettaa, että suurin summa on positiivinen.
Voit olettaa, että käyttäjä antaa ainakin yhden luvun ja korkeintaan miljoona lukua. Lisäksi voit olettaa, että jokainen luku on kokonaisluku väliltä -1000...1000, jolloin kaikki summat mahtuvat int
-muuttujiin. Tee ohjelmasta niin tehokas, että se käsittelee miljoona lukua muutamassa sekunnissa. Huom! Ohjelmasi on oltava tehokas. Testit tarkastavat asian.
Kuinka monta? 5 Anna luvut: 1 -2 5 2 -1 Suurin summa: 7
Kuinka monta? 5 Anna luvut: 4 -2 5 2 -1 Suurin summa: 9
Kuinka monta? 4 Anna luvut: 4 3 -5 6 Suurin summa: 8
Esimerkissä 1 suurin summa saadaan laskemalla yhteen luvut 5 ja 2. Esimerkissä 2 suurin summa saadaan laskemalla yhteen luvut 4, -2, 5 ja 2. Esimerkissä 3 suurin summa saadaan laskemalla kaikki luvut yhteen.
Vihje: Tehtävään on olemassa ratkaisualgoritmi, jonka aikavaativuus on O(n) ja tilavaativuus on O(1).
Tehtäväpohjassa on luokka Polynomi
, joka esittää polynomia linkitetyn listan avulla. Täydennä toteutusta metodilla summaa
joka laskee kahden polynomin summan.
Esimerkkejä polynomien yhteenlaskuista:
3x^2 + 5x + 1 + 7x^3 + 7x^2 = 7x^3 + 10x^2 + 5x + 1 7x^100 + x^49 + 1 + 18x^100 + x^50 + 1 = 25x^100 + x^50 + x^49 + 1
DNA on oleellisesti ottaen pitkä merkkijono, joka koostuu merkeistä A
, C
, G
ja T
.
Tehtävänäsi on tulostaa kaikki annetun mittaiset DNA:t. Esimerkiksi pituuden 2 DNA:t ovat:
AA AC AG AT CA CC CG CT GA GC GG GT TA TC TG TT
Vastaavasti pituuden 3 DNA:t ovat (välilyönnein eroteltuina):
AAA AAC AAG AAT ACA ACC ACG ACT AGA AGC AGG AGT ATA ATC ATG ATT CAA CAC CAG CAT CCA CCC CCG CCT CGA CGC CGG CGT CTA CTC CTG CTT GAA GAC GAG GAT GCA GCC GCG GCT GGA GGC GGG GGT GTA GTC GTG GTT TAA TAC TAG TAT TCA TCC TCG TCT TGA TGC TGG TGT TTA TTC TTG TTT
Toteuta tehtäväpohjaan metodi tulostaKaikkiDNAt(int pituus)
joka tulostaa kaikki annetun pituiset DNAt.
Vihje! Tämä hoituu helposti rekursiolla.
Nytkin tuotamme DNA:ta, mutta lisärajoituksella. Haluamme kaikki annetun pituiset DNA-sekvenssit joissa ei esiinny kahta samaa kirjainta peräkkäin.
Pituus 2:
AC AG AT CA CG CT GA GC TC TG
Pituus 3:
ACA ACG ACT AGA AGC AGT ATA ATC ATG CAC CAG CAT CGA CGC CGT CTA CTC CTG GAC GAG GAT GCA GCG GCT GTA GTC GTG TAC TAG TAT TCA TCG TCT TGA TGC TGT
Toteuta tehtäväpohjaan metodi tulostaDNAt(int pituus)
joka tulostaa kaikki annetun pituiset DNA-merkkijonot joissa ei esiinny samaa merkkiä kahta kertaa peräkkäin. %p Voit tulostaa DNA:t missä tahansa järjestyksessä. Erottele DNA:t joko rivinvaihdoilla tai välilyönneillä.
Toteuta ohjelman SamatSummat
metodi boolean samatSummat(int[] luvut)
, joka tarkistaa, voiko käyttäjän antamat luvut jakaa kahteen ryhmään niin, että molemmissa ryhmissä lukujen summa on sama. Jokaisen luvun on siis kuuluttava jompaan kumpaan ryhmään, ja lisäksi yksi luku ei voi kuulua molempiin ryhmiin. Voit olettaa, että käyttäjä antaa ainakin yhden ja korkeintaan 20 lukua. Lisäksi voit olettaa, että jokainen luku on positiivinen kokonaisluku ja mahtuu mahtuu int muuttujaan.
Vihjeitä ohjelman tekemiseen on luentomateriaalin sivuilla 95-96.
Vihje! suoraviivainen rekursiivinen algoritmi riittää tässä tehtävässä!
Kuinka monta? 3 Anna luvut: 4 2 6 Jako on mahdollinen.
Kuinka monta? 3 Anna luvut: 4 2 5 4 Jako ei ole mahdollinen.
Esimerkissä 1 toiseen ryhmään voidaan valita luvut 4 ja 2 ja toiseen ryhmään luku 6. Tällöin kummassakin ryhmässä lukujen summa on 6.
Esimerkissä 2 jako ei ole mahdollinen. Parittomia lukuja on tasan yksi, joten missä tahansa jakotavassa toisen ryhmän summa on parillinen ja toisen ryhmän summa on pariton.
Tällaisia kahteen ryhmään jakoja kutsutaan myös osituksiksi (engl. partition). Tämä ongelma on varsinainen klassikko, ja se tunnetaan yksinkertaisesti nimellä PARTITION. Ongelma on erikoistapaus vielä kuuluisammasta ongelmasta SUBSET-SUM. Kummatkin ongelmat ovat lisäksi NP-täydellisiä. Tässä tehtävässä ei kuitenkaan tarvitse toteuttaa monimutkaista ja tehokasta internetistä löytämääsi ratkaisua vaan suoraviivainen rekursiivinen algoritmi riittää
Tehtäväpohjan mukana tulee luokka Node
, joka esittää yhteen suuntaan linkitetyn listan solmua:
public class Node { private Node next = null; private int value; public Node(int value, Node next) { this.next=next; this.value=value; } public Node(int value) { this.value=value; } public int getValue() { return value; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } public Node setValue(int value) { this.value = value; } }
Toteuta luokkaan Operaatioita
seuraavat metodit:
Node reverse(Node list)
kääntää annetun listanNode cut(Node list, int i)
"leikkaa" listan indeksin i
kohdalta ja palauttaa loppuosan. Esimerkki:// Rakennetaan lista [6,7,8,9,10] Node lista = new Node(6,new Node(7, new Node(8, new Node(9, new Node(10))))); // Katkaistaan indeksistä 2 Node loppu = Operaatioita.cut(lista, 2); // Nyt loppu on [8,9,10] ja lista on [6,7]
Huom! Kummankaan metodin ei tule allokoida uusia solmuja, vaan annetun listan solmut tulee uudelleenkäyttää! Kummassakaan metodissa ei siis tarvitse olla yhtään new Node
-kutsua!
Huom! Testit testaavat että pitkänkin listan kääntämiseen menee alle 20ms.
Tarkastellaan peliä, jossa joukko henkilöitä on asettunut rinkiin. Pelissä henkilöitä käydään läpi myötäpäivään ja joka toinen henkilö joutuu poistumaan ringistä. Ensimmäinen poistuja on toisena oleva. Lopulta ringissä on jäljellä enää yksi henkilö, josta tulee pelin voittaja.
Esimerkiksi jos henkilöitä on viisi, tilanne on seuraavanlainen:
Henkilöt käydään läpi seuraavasti:
Pelin voittaja on henkilö 3, joka jää rinkiin viimeisenä.
Toteuta ohjelmaan Viimeinen
metodi int viimeinen(int montako)
, joka selvittää yllä kuvatun pelin voittajan, kun pelissä on annettu määrä henkilöitä.
Huom! Suunnittele ohjelma niin, että se on tehokas myös miljoonan henkilön tapauksessa. Ratkaisun tulee olla O(1) ja O(n log n) välillä.
Kuinka monta? 5 Voittaja: 3
Kuinka monta? 6 Voittaja: 5
Palindromi on merkkijono joka on sama kumminkin päin luettuna. Esimerkiksi sana anna on palindromi mutta sana talo ei. Kirjaimista A
...C
voidaan muodostaa seuraavat 3 kirjaimen pituiset palindromit:
AAA
ABA
ACA
BAB
BBB
BCB
CAC
CBC
CCC
Toteuta metodi void palindromit(int kirjaimia, int pituus)
, joka tulostaa kaikki pituus
-pituiset palindromit, jotka muodostuvat kirjaimia
kpl aakkosten ensimmäisistä kirjamesta.
Voit olettaa, että kirjaimia on korkeintaan 26 ja pituus on korkeintaan 100.
Huom! älä tee niin että generoit merkkijonon ja sitten tarkistat onko se palindromi. Kaikkia merkkijonoja on paljon enemmän kuin palindromeja. Voit nähdä tämän itse kirjoittamalla paperille kaikki pituuden 3 merkkijonot merkeistä A,B,C ja kaikki pituuden 3 palindromit merkeistä A,B,C.
Kutsu tulosta(3,3)
tulostaa:
AAA ABA ACA BAB BBB BCB CAC CBC CCC
Kutsu tulosta(2,4)
tulostaa:
AAAA ABBA BAAB BBBB
Tehtäväpohjassa on binääripuun toteutus. Puun solmut esitetään luokan Node
avulla:
public class Node { private Node left; private Node right; private int value; public Node(int value, Node left, Node right) { this.value = value; this.left = left; this.right = right; } public Node(int value) { this.value = value; // left ja right ovat null } public Node getLeft() { return left; } public Node getRight() { return getRight; } public int getValue() { return value; } }
Toteuta luokkaan Puita
seuraavat metodit:
int countLeaves(Node tree)
laskee puun lehtien määrän kun sille annetaan puun juurisolmuint treeHeight(Node tree)
laskee puun korkeuden kun sille annetaan puun juurisolmuint sum(Node tree)
laskee puun solmuissa olevien arvojen (value
) summanTämän puun:
1 / \ 2 3 / \ \ 4 5 6 / \ 7 8
Tämän puun:
0 / \ 1 0 / \ 0 1 / \ 1 0 / \ 1 1
Tarkastellaan seuraavaa lukupyramidia:
4 3 2 1 3 5 3 4 1 1
Tehtävänä on etsiä reitti pyramidin huipulta alas, jossa joka vaiheessa siirrytään alavasemmalla tai alaoikealla olevaan lukuun ja jossa lukujen summa on mahdollisimman suuri. Voit kuitenkin olettaa, että lukujen lopullinen summa mahtuu int
-muuttujaan.
Tässä tapauksessa paras reitti aloittaa huipulta (4), liikkuu alavasemmalle (3), liikkuu alaoikealle (3) ja liikkuu alavasemmalle (4). Lukujen summa reitillä on 14, ja millään muulla reitillä summa ei ole yhtä suuri.
Seuraava kuva vastaa reittiä:
4 3 2 1 3 5 3 4 1 1
Toteuta tehtäväpohjan ohjelmaan ParasReitti.java
metodi public static int parasReitti(int[][] pyramidi)
, joka etsii parhaan reitin lukupyramidin huipulta alas ja ilmoittaa lukujen summan kyseisellä reitillä.
Metodi saa syötteenä kaksiulotteisen taulukon pyramidi
siten, että pyramidi[i][j]
on pyramidin (ylhäältä laskien, nollasta lähtien) i
:nnen rivin (vasemmalta laskien, nollasta lähtien) j
:nnes numero.
Ohjelma voi olettaa, että pyramidissa on korkeintaan sata kerrosta. Tällöinkin ohjelman toiminnan tulee olla tehokas. Aikavaativuuden tulee olla kutakuinkin O(n^2)
Anna koko: 4 Anna luvut: 4 3 2 1 3 5 3 4 1 1 Paras: 14
Anna koko: 5 Anna luvut: 7 2 9 1 5 2 3 1 4 2 7 8 3 1 4 Paras: 30
Vihje! Tässä tehtävässä saattaa olla iloa ohjelmontitekniikasta nimeltä dynaaminen ohjelmointi (dynamic programming).
Toteuta metodi int evaluoi(String lauseke)
, joka laskee käyttäjän antaman laskutoimituksen. Laskutoimitus voi sisältää kokonaislukuja väliltä 0...1000, operaatioita + ja * sekä sulkuja. Voit olettaa, että laskutoimituksen jokainen välivaihe mahtuu int
-muuttujaan.
Ohjelmasi voi olettaa, että laskutoimituksen muoto on oikea (eli ohjelman ei tarvitse sisältää virheenkäsittelyä). Laskutoimitus muodostuu merkeistä 0–9, +, *, ( ja ), eikä siinä ole ylimääräisiä välilyöntejä.
Anna laskutoimitus: 2+5*3 Tulos: 17
Anna laskutoimitus: (1+1)*5 Tulos: 10
Anna laskutoimitus: 2*(3*(5+2)+1) Tulos: 44
Anna laskutoimitus: 100*10*10 Tulos: 10000
Vihje! Yksi algoritmi tehtävän ratkaisuun tunnetaan nimellä "shunting yard". Saattaa olla kuitenkin hauskempaa keksiä itse sopiva algoritmi!
Tehtäväpohjassa on binäärihakupuun toteutus. Puun solmut esitetään
luokan
Node
avulla. Luokka node tulee tehtäväpohjan mukana.
Esitämme puun
Tree
-luokalla,
joka sisältää viitteen juurisolmuun.
Ensimmäisenä tehtävänäsi on toteuttaa luokkaan
Hakupuu
metodi
public static Object search(Tree tree, int key)
,
joka hakee annettua avainta vastaavan arvon annetusta
binääripuusta.
Toisena tehtävänäsi on toteuttaa metodi
public static void
insert(Tree tree, int key, Object value)
,
joka lisää
annetun arvon annetulla avaimella puuhun. Jos avain on jo
käytössä, päivitetään sitä vastaava arvo.
Huom!
tässä tehtävässä törmäät
tyhjiin
puihin.
Tyhjä puu on
Tree
-olio
jonka
root
on
null
.
Apua löydät luentokalvojen sivuilta 169-171.
Operaatioidesi tulee toimia alle 50 millisekunnissa isoillakin puilla.
Lisätään tyhjään puuhun järjestyksessä avaimet 3,7,5,2,0,8,6. Tässä ovat puun vaiheet:
3 3 \ 7 3 \ 7 / 5 3 / \ 2 7 / 5 3 / \ 2 7 / / \ 0 5 8 3 / \ 2 7 / / \ 0 5 8 \ 6
Toteuta luokka
SanaTilasto
,
joka laskee annetun
tekstin sanojen frekvenssit (eli esiintymiskerrat). Toteutuksesi tulee olla seuraavan pohjan mukainen:
public class SanaTilasto { public SanaTilasto(Scanner lukija) { // lue sanat käyttäen lukija.next()iä } public int frekvenssi(String sana) { // palauttaa annetun sanan frekvenssin } public int eriSanojenLukumaara() { // palauttaa _eri_ sanojen lukumäärän } }
Käytä tehtävässä Javan valmista hajautustaulua
HashMap
.
Tehtävänäsi on tulostaa binääripuu seuraavalla tavalla. Puu:
1 / \ 2 3 / \ \ 4 5 6 / \ 7 8
Tulostus:
_1_ _2_ 3_ 4 5 _6_ 7 8
Puun solmut siis tulostetaan tasojärjestyksessä ja lisäksi jokaiseen solmuun merkitään onko sillä vasenta ja/tai oikeaa lasta.
Toteuta tehtäväpohjaan metodi
print(Tree tree)
joka
tekee tulostuksen. Luokat
Tree
ja
Node
ovat
samat kuin tehtävässä 1. Tulosta puun solmuista vain avain, arvon
(value
) voit unohtaa.
Vihje!
Javan
ArrayDeque
-luokka
(jono) saattaa
auttaa.
Tehtäväsi on tunnistaa annetusta binäärihakupuusta täyttääkö se
AVL-ehdon (ks. luentokalvojen sivut 202 ja 294). Toteuta metodi
public
static boolean isAVL(Tree tree)
.
Huom! puuhun ei ole tallennettu alipuitten korkeuksia, joten sinun täytyy laskea ne itse.
Huom! sinun ei tarvitse tarkistaa, onko annettu puu hakupuu! Et siis tarvitse solmujen avaimia tai arvoja mihinkään.
o o o / \ / \ / \ o o o o o o /\ /\ / / \ / \ |\ o o o o o o o o o o o / \ \ / \ / \ o o o o o o o
o o o / \ / \ / \ o o o o o o /\ /\ / / \ / \ \ o o o o o o o o o o / \ / \ / \ / \ / \ o o o o o o o o o o \ o
Tarkastellaan seuraavaa binääripuuta:
7 / \ 9 2 / \ 3 5
Luentomateriaalin sivuilla 161-168 määritellään esijärjestys, sisäjärjestys ja jälkijärjestys.
Puun solmut esijärjestyksessä:
79352
Puun solmut sisäjärjestyksessä:
39572
Puun solmut jälkijärjestyksessä:
35927
Tee ohjelma metodi
int[] jalkijarjestys(int[] esi, int[]
sisa)
,
jolle annetaan binääripuun esijärjestys ja sisäjärjestys
ja joka muodostaa niiden perusteella binääripuun
jälkijärjestyksen.
Huom! Voit olettaa että sama solmun numero ei toistu puussa.
E: 7 9 3 5 2 S: 3 9 5 7 2 J: 3 5 9 2 7
Tämän esimerkin puu on:
0 / \ / \ 1 2 / \ / \ 3 4 7 8 / \ | 5 6 9 | 10
E: 0 1 3 4 5 10 6 2 7 8 9 S: 3 1 10 5 4 6 0 7 2 9 8 J: 3 10 5 6 4 1 7 9 8 2 0
Tehtävänäsi on tarkistaa, löytyykö linkitetystä
listasta
sykli
eli silmukka. Tehtäväpohjassa on linkitetyn
listan toteutus tutulla
Node
-luokalla:
Toteuta metodi
int sykli(Node alku)
joka palauttaa
annetusta listasta löytyvän syklin pituuden tai
0
jos
sykliä ei löydy.
Tähän tehtävään on monia erilaisia ratkaisuita. Ratkaisusi ei tarvitse olla erityisen tehokas, mutta ongelmaan on olemassa ratkaisu jonka aikavaativuus on O(n) ja tilavaativuus O(1).
Huom!
syklin tunnistamiseksi pitää
tarkastella
Node
jen
yhtäsuuruutta,
pelkkä
Node
issa
olevien arvojen tarkasteleminen ei
riitä.
Luodaan seuraavanlainen lista:
1 --> 2 --> 3 --> 1 --> 5 --> 1 --> 900 ^__________________|
Javakoodina:
Node vika = new Node(900); Node sykli = new Node(1, new Node(5, new Node(1, vika))); vika.setNext(sykli); Node lista = new Node(1, new Node(2, new Node(3, sykli)));
Nyt kutsu
sykli(lista)
palauttaisi
4
Saat syötteenä binääripuun. Muodosta lähes täydellinen binääripuu , jonka esijärjestys on sama kuin syötteenä olevan binääripuun. Lähes täydellinen binääripuu tarkoittaa binääripuuta, jonka kaikki lehdet ovat kahdella vierekkäisellä tasolla, ja lisäksi lehdet on pakattu vasemmalle. Lisäksi kaikilla lehtitasoja korkeammilla tasoilla olevilla solmuilla on kaksi lasta.
Lähes täydellinen binääripuu on siis täydellinen binääripuu jolta on poistettu osa sen oikeanpuolimmaisista lehdistä. Lähes täydellinen binääripuu vastaa luentokalvojen sivun 339 ehtoa K1.
Kirjoita metodi
Node muunna(Node tree)
joka tekee
tämän muunnoksen.
o o o o o / \ / \ / \ / \ / \ o o o o o o o o o o / \ / \ / \ / / \ / / \ / \ o o o o o o o o o o o o o o / \ o o
Syöte Tulos 1 1 \ / \ 2 2 5 \ / \ / 3 3 4 6 \ 4 \ 5 \ 6
Syöte Tulos 8 8 / \ / \ 3 9 3 7 / \ \ / \ / \ 2 6 10 2 5 9 10 / / \ / \ 1 5 7 1 6
Shakissa kuningatar voi liikkua vaaka-, pysty- tai vinosuuntaisesti. Tehtävänäsi on laskea, kuinka monella tavalla kaksi kuningatarta voidaan sijoittaa n x n -shakkilaudalle niin, että ne eivät uhkaa toisiaan.
Esimerkiksi tapauksessa n = 3 mahdollisuudet ovat:
K.. K.. .K. .K. ..K ..K ... ... ..K ... ... ... K.. ... K.. ..K ... .K. K.. ..K ... .K. ..K K..
Tapauksessa n = 10 mahdollisuuksia on jo 3480.
Huomaa että isommilla syötteillä tulos ei mahdu Int -muuttujaan.
Toteuta luokan
Kuningattaret
metodi
long kuningatarLaskija(int leveys)
joka
laskee kuningatarten määrän.
Anna koko: 10 Vastaus: 3480
Kiertopelissä 3x3-ruudukko sisältää luvut 1–9. Tavoitteena on saattaa ruudukon luvut seuraavaan järjestykseen:
1 2 3 4 5 6 7 8 9
Ruudukon lukujen paikkoja voi muuttaa kiertojen avulla. Yksi kierto kohdistuu johonkin 2x2-aliruudukkoon ja kiertää sen sisällä olevia lukuja askeleen myötä- tai vastapäivään.
Tarkastellaan esimerkiksi seuraavaa pelitilannetta:
2 8 3 1 4 5 7 9 6
Kierretään ensin ylävasemmalla olevaa 2x2-ruudukkoa myötäpäivään:
2 8 3 1 2 3 1 4 5 => 4 8 5 7 9 6 7 9 6
Kierretään sitten alaoikealla olevaa 2x2-ruudukkoa vastapäivään:
1 2 3 1 2 3 4 8 5 => 4 5 6 7 9 6 7 8 9
Tässä tapauksessa siis kaksi kiertoa riitti kiertopelin ratkaisuun.
Tee ohjelma
Kiertopeli.java
,
jolle annetaan kiertopelin aloitustilanne
ja joka tulostaa pienimmän kiertojen määrän,
joilla ruudukon luvut saa järjestykseen.
Tee ohjelmastasi sellainen, että se ratkaisee minkä tahansa kiertopelin aloitustilanteen silmänräpäyksessä.
Anna ruudukko: 2 8 3 1 4 5 7 9 6 Tulos: 2
Anna ruudukko: 1 6 5 8 7 2 3 4 9 Tulos: 4
Opiskelijanumeron viimeinen numero on tarkistusnumero, joka saadaan laskemalla yhteen alkuosan numerot, jotka on kerrottu luvuilla 1, 3, 7, 1, 3, 7, ..., 1, 3 ja 7. Tarkistusnumero on etäisyys näin saadusta luvusta seuraavaan tasakymmeneen.
Huom! kertoimet tasataan vasemmalle. Viimeisen numeron kerroin on siis aina 7.
Esimerkiksi jos opiskelijanumeron alkuosa on 01274913, lasketaan 3*0 + 7*1 + 1*2 + 3*7 + 7*4 + 1*9 + 3*1 + 7*3 = 91, josta on matkaa tasakymmeneen 9. Tämän vuoksi tarkistusnumero on 9 ja koko opiskelijanumero on 012749139.
Tee metodi
int tarkiste(String alkuosa)
,
jolle
annetaan opiskelijanumeron alkuosa ja joka laskee sen
tarkistusnumeron. Opiskelijoitten määrän kasvamisen varalta tee
metodistasi sellainen joka tukee kaikenmittaisia
opiskelijanumeroita.
Vihje!
Character.digit
-metodi
on hyödyllinen.
Alkuosa: 01274913 Tarkistusnumero: 9
Alkuosa: 01525594 Tarkistusnumero: 7
Alkuosa: 1 Tarkistusnumero: 3
Alkuosa: 001 Tarkistusnumero: 3
Alkuosa: 1234567891011121314151617181920 Tarkistusnumero: 4
Stirlingin luvut ovat sukua laskareissa käsitellyille binomikertoimille. Stirlingin luvun S(n,k) voi laskea kaavalla:
S(n+1,k) = k*S(n,k) + S(n,k-1)
Reunaehdot ovat:
S(0,0) = 1,
S(n,0) = S(0,k) = 0
Toteuta metodi
long stirling(long n, long k)
joka
laskee luvun
S(n,k).
Huom!
long
on javan kokonaislukutyyppi, johon mahtuu paljon suurempia lukuja kuin tyyppiin
int
.
Vihje! Tämä on helppoa rekursiolla!
Tarkastellaan seuraavaa kirjainruudukkoa:
RIRA ATIA TATR
Sana TIRA voidaan lukea ruudukosta neljällä tavalla:
RI.. AT.. ....
.IRA .T.. ....
..RA .TI. ....
..RA ..I. ..T.
Tee metodi
int kirjainRuudukko(char[][] ruudukko, String
sana)
,
jolle annetaan kirjainruudukko ja etsittävä sana ja joka
ilmoittaa, kuinka monella tavalla sana voidaan lukea ruudukosta. Sanan
lukemisen saa aloittaa mistä tahansa ruudusta, ja jokaisen kirjaimen
jälkeen siirrytään askel ylös, alas, vasemmalle tai oikealle. Sama
ruudukon kirjain voi esiintyä monta kertaa sanassa.
Voit olettaa, että ruudukon leveys ja korkeus ovat korkeintaan 100, sanan pituus on korkeintaan 20, kaikki kirjaimet ovat joukossa A...Z ja sana esiintyy ruudukossa korkeintaan 1000000 kertaa.
Anna korkeus: 3 Anna leveys: 4 Anna ruudukko: RIRA ATIA TATR Anna sana: TIRA Tulos: 4
Anna korkeus: 2 Anna leveys: 2 Anna ruudukko: AA AA Anna sana: AAAAAAAAAA Tulos: 2048
Tee metodi
void ratkaise(int[][] sudoku)
,
joka
ratkaisee annetun sudokun. Sudoku annetaan
kaksiulotteisena
int
-taulukkona.
Taulukossa 0
tarkoittaa tyhjää paikkaa ja numerot 1-9 tarkoittavat numeroita 1-9.
Metodin pitäisi muokata annettua taulukkoa niin että se on oikein
täytetty sudoku, eli:
Voit olettaa, että metodille annettuun sudokuun on olemassa yksikäsitteinen ratkaisu.
Suomalainen Arto Inkala on laatinut "maailman vaikeimman Sudokun":
Tee ohjelmastasi sellainen, että se ratkaisee kyseisen sudokun (ja muutkin löytämäsi sudokut) silmänräpäyksessä.
Vihje! Luentokalvojen kuningattaret shakkilaudalla -esimerkki.
(Tämän pitäisi ratketa hyvin nopeasti)
Anna sudoku: 8??93???2 ??9????4? 7?21??96? 2??????9? ?6?????7? ?7???6??5 ?27??84?6 ?3????5?? 5???62??8 Ratkaisu: 846937152 319625847 752184963 285713694 463859271 971246385 127598436 638471529 594362718
Anna sudoku: ??53????? 8??????2? ?7??1?5?? 4????53?? ?1??7???6 ??32???8? ?6?5????9 ??4????3? ?????97?? Ratkaisu: 145327698 839654127 672918543 496185372 218473956 753296481 367542819 984761235 521839764
Anna sudoku: ????????? ?????3?85 ??1?2???? ???5?7??? ??4???1?? ?9??????? 5??????7? ??2?1???? ????4???9 Ratkaisu: 237658914 469173285 851924367 123567498 784239156 695481732 546392871 972815643 318746529
Toteuta metodi
boolean etsi(int[] taulukko, int k)
joka tutkii onko taulukossa kahta lukua, joiden summa
on
k
.
Sama taulukon luku ei saa kuulua summaan kahta
kertaa. Ratkaisusi tulee olla tehokas.
Huom! syötteessä voi olla myös negatiivisia lukuja!
Vihje! on olemassa ajassa O(n log n) toimiva ratkaisu.
Toteuta metodi
boolean etsi(int[] taulukko, int k)
joka tutkii onko taulukossa
neljää
lukua, joiden summa
on
k
.
Tässä tehtävässä sama taulukon
luku
saa
kuulua summaan useamman kerran. Ratkaisusi tulee
olla tehokas.
Huom! syötteessä voi olla myös negatiivisia lukuja!
Vihje! on olemassa ajassa O(n 2 log n) toimiva ratkaisu.
Kaikissa näissä esimerkeissä
k=10
.
Näille taulukoille
etsi
palauttaa
true
:
{2,3} {1,2,3,4} {4,2,3,1} {4,4,1,1,1,6,6} {4,3,1,5,5,6,6}
Näille taulukoille
etsi
palauttaa
false
:
{5} {1,1,1,1} {4,6,5,5} {6,4,5,5} {6,6,6,4} {9,1,1,1,1,5,6}
Tämä tehtävä käsittelee Youngin taulukoita, joista oli tehtävä viimeuoden laskareissa, kts. https://www.cs.helsinki.fi/u/floreen/tira2012/teht07.pdf tehtävä 5
Mikäli youngin taulukko ei ole tuttu, kts. https://en.wikipedia.org/wiki/Young_tableau
Tehtävänäsi on toteuttaa metodi
boolean etsi(int[][] young,
int k)
,
joka tutkii löytyykö luku
k
annetusta Youngin taulukosta. Ratkaisusi aikavaativuuden tulisi
olla
O(m+n),
jossa
m
ja
n
ovat
Youngin taulukon leveys ja korkeus.
Joukko ihmisiä jonottaa lääkärin vastaanotolle. Kun ihminen saapuu odotushuoneeseen, sairaanhoitaja arvioi hänen hoidontarpeensa. Hoidontarvetta kuvaa kokonaisluku 1-1 000 000. Kun lääkäri on vapaa, valitsee hän hoidettavakseen sen odottajan, jolla on suurin hoidontarve .
Toteuta luokka
Odotushuone
,
jonka olioilla on metodit:
void lisaa(String nimi, int tarve)
lisää odotushuoneeseen odottajan
String seuraavaPotilas()
palauttaa odotushuoneen suurimman hoidontarpeen omaavan potilaan nimen ja
poistaa potilaan odotushuoneesta
Kummankin metodin täytyy toimia hyvin nopeasti.
Voit olettaa, ettei tyhjästä odotushuoneesta koiteta hakea potilasta, ja että kahdella potilaalla ei ole samaa nimeä.
Vihje!
Javasta löytyy luokka
PriorityQueue
.
Tarvinnet myös rajapintaa
Comparator
tai
Comparable
.
Odotushuone o = new Odotushuone(); o.lisaa("Pekka",3); o.lisaa("Liisa",4); System.out.println(o.seuraavaPotilas()); o.lisaa("Simo",1); o.lisaa("Kerttu",99); System.out.println(o.seuraavaPotilas()); System.out.println(o.seuraavaPotilas()); System.out.println(o.seuraavaPotilas());
Ylläoleva koodi tulostaa:
Liisa Kerttu Pekka Simo
Tehtävänäsi on paeta vankilasta hiippailemalla ja kaivamalla tunneleita. Vankila on kaksiulotteinen ruudukko jossa on
muureja
(#
),
käytäviä
(.
) ja yksi
aloituspaikka
(X
). Tässä on esimerkki vankilasta:
#..## #.#.# ##X#. #..## .##.#
Vanki aloittaa aloituspaikasta ja voi liikkua neljään pääilmansuuntaan. Muuriruutuun liikkuminen on kaivautumista ja käytäväruutuun liikkuminen hiippailemista. Tehtävänäsi on etsiä paras reitti aloituspaikasta ruudukon reunalle. Paras reitti on sellainen jossa on
Tämä tarkoittaa että kahdesta reitistä parempi on se jossa on vähemmän kaivamista. Jos kahdessa reitissä on saman verran kaivamista, on niistä parempi se jossa on vähemmän hiippailemista (eli kumpi on lyhempi). Parhaita reittejä voi olla useita.
Toteuta metodi
char[] pakene(char[][] vankila)
joka
laskee lyhimmän reitin. Syötteenä on
kaksiulotteinen
char
-taulukko
joka kuvaa vankilaa
(
'.'
on käytävä,
'#'
on
muuri,
'X'
on aloituspaikka). Algoritmin tulee
palauttaa lyhin reitti
char
-taulukkona
joka kuvaa
reittiä:
'Y'
on ylöspäin
liikkuminen,
'A'
alaspäin
liikkuminen,
'V'
ja
'O'
vasemmalle ja
oikealle liikkumisia.
Huom!
ratkaisun tehokkuudella ei tässä tehtävässä ole
niin väliä. Suurin syöte on kokoa
10x10
.
Vankila:
###.# #...# #.### #..X# #####
Paras reitti (ei kaivautumista):
VVYYOOY
#.### #.#.# ##X## #..## .##.#
AA
######### ######### ###.#...# ###.##### ###..X### ######### ######### #########
YYOOO
Toteuta luokka
Henkilorekisteri
,
jolla on metodit
void lisaa(String etu, String suku, int ika)
lisää henkilön
boolean kysele(String etu, String suku, int ika)
tarkistaa onko kyseinen henkilö rekisterissä
ArrayList<String> haeKaikki()
palauttaa
kaikkien lisättyjen henkilöitten kuvaukset muodossa
"Etu Suku
ikä"
.
Kuvaukset tulee palauttaa seuraavassa järjestyksessä:
Vihje! käytä jotain valmista tietorakennetta!
Tehtävänäsi on järjestää luvuista
-100 - +100
koostuva taulukko
lineaarisessa
ajassa. Toteuta siis
metodi
sort(int[] taulukko)
.
Toteuta metodi
int suurin(int k, int[] luvut)
,
joka
palauttaa taulukon
luvut
k
:nneksi
suurimman
luvun.
Ratkaisusi täytyy olla nopea sekä
n
:n
(taulukon
pituus) että
k
:n
suhteen. On olemassa useita
luokkaa
O(nlog
k)
olevia ratkaisuita.
Voit olettaa että taulukossa ei toistu mikään luku.
Vihje! valitse sopiva tietorakenne johon "keräät" lukuja.
Vihje! luokkaa O(nlog n) oleva ratkaisu (esim. järjestäminen) ei riitä!
Saat joukon koordinaatteja. Tehtävänäsi on tutkia, ovatko jotkin kaksi koordinaattia samalla origon (eli pisteen (0,0)) kautta kulkevalla suoralla. Ratkaisusi tulisi olla luokkaa O(nlog n).
Tehtävän yksinkertaistamiseksi voit olettaa, että kaikki x- ja y- arvot ovat positiivisia kokonaislukuja.
Toteuta metodi
boolean suora(int[][] pisteet)
,
joka
saa parametrinaan
n
pistettä ja palauttaa
true
jos suora löytyi. Pisteet esitetään
kaksiulotteisena
int
-taulukkona
siten, että ensimmäisen
pisteen koodinaatit ovat
(pisteet[0][0]
,pisteet[0][1]
), toisen
pisteen (pisteet[1][0]
,pisteet[1][1]
),
jne.
Selvennys:
pisteet[i][0]
sisältää pisteen nro
i x-koordinaatin ja
pisteet[i][1]
vastaavasti
y-koordinaatin
Vihje! järjestäminen!
Suorakulmion muotoinen peltoalue muodostuu neliöruuduista. Alueelle halutaan rakentaa mahdollisimman suuri neliön muotoinen luistinrata. Kuitenkin joissakin peltoalueen ruuduissa on puu, joiden kohdalle luistinrataa ei voi rakentaa.
Tee metodi
int suurinNelio(boolean[][] kartta)
,
jolle
annetaan peltoalueen kuvaus ja joka laskee suurimman neliön sivun
pituuden. Peltoalue esitetään
kaksiulotteisena
boolean
-taulukkona,
jossa
true
tarkoittaa vapaata kohtaa
ja
false
tarkoittaa puuta.
Tarkastellaan esimerkiksi seuraavaa tilannetta:
...P.. .P.... P....P .P...P
Tässä peltoalueen koko on 4 x 6 ruutua. Merkki
.
tarkoittaa tyhjää ruutua ja merkki
P
tarkoittaa puuta.
Tässä tapauksessa suurimman luistinradan koko on 3 x 3 ruutua.
Seuraavassa luistinrataa kuvaa merkki
L
:
...P.. .PLLL. P.LLLP .PLLLP
Voit olettaa, että peltoalueen koko on korkeintaan 1000 x 1000 ruutua. Tee ohjelmastasi tehokas: aikavaativuuden olisi syytä olla O(n · m), jossa n on alueen korkeus ja m on alueen leveys. Käytännössä ohjelmasi tulisi siis käydä peltoalueen ruudut kerran läpi ja ilmoittaa sitten vastaus.
Korkeus: 4 Leveys: 6 Anna kuvaus: ...P.. .P.... P....P .P...P Tulos: 3
Korkeus: 5 Leveys: 3 Anna kuvaus: ... ... .P. ... ... Tulos: 2
Saat talon pohjapiirustuksen. Tehtävänäsi on laskea, montako huonetta talossa on. Huone tarkoittaa yhtenäistä seinien rajaamaa lattiatilaa.
Toteuta metodi
int huoneet(char[][] talo)
,
joka
palauttaa huoneitten lukumäärän. Pohjapiirroksessa
merkki
'#'
tarkoittaa seinää ja merkki
'.'
lattiaa.
Huom: juuri kartan reunojen ulkopuolella on (implisiittiset) seinät. Siis esimerkiksi seuraavassa kartassa on kaksi huonetta:
. # .
########## #.###....# #..#####.# #...#.##.# ##########
Huoneita: 3
########## #.#...##.# #.#.#....# #.....##.# ##########
Huoneita: 1
Tällä kertaa huoneitten laskemisen sijaan on tehtävänä laskea
huoneitten koot. Toteuta metodi
ArrayList<Integer>
koot(char[][] talo)
joka palauttaa huoneitten koot (jossain
järjestyksessä) ArrayListissä.
Talon pohjapiirros annetaan ohjelmalle samassa muodossa kuin edellisessä tehtävässä.
Pohjapiirros:
########## #.###....# #..#####.# #...#.##.# ##########
Huoneiden koot: 6,6,1
Tässä talossa on kaksi huonetta, joiden koko on 6 ruutua, ja yksi huone, jonka koko on 1 ruutu.
Pohjapiirros:
########## #.#..#.#.# ######.### #..#.#.#.# ##########
Huoneiden koot: 3,2,2,1,1,1,1
Huom! testissä ollut 5,5,4,4-tapaus korjattu!
Ratsu liikkuu shakkilaudalla kulkemalla kaksi ruutua johonkin
suuntaan (vasen, oikea, ylös alas), ja sitten yhden ruudun edellisen
suunnan kanssa suorassa kulmassa olevaan suuntaan. Seuraavassa on
piirretty ratsun (R
) kaikki mahdolliset seuraavat
ruudut (x
):
........ ........ ..x.x... .x...x.. ...R.... .x...x.. ..x.x... ........
Tehtävänäsi on etsiä ratsun lyhin reitti annetusta 8x8
shakkilaudan ruudusta toiseen. Toteuta metodi
int ratsu(int
x0, int y0, int x1, int y1)
,
joka palauttaa lyhimmän reitin
pituuden lähtöruudusta
(x0,y0)
maaliruutuun
(x1,y1)
.
ratsu(0,0,1,2)=1
0....... ........ .1...... ........ ........ ........ ........ ........
ratsu(0,0,7,7)=6
(samanpituisia reittejä on toki useita)
0....... ........ .1...... ...2.... .....3.. .......4 .....5.. .......6
Talossa on hajonnut vesiputkia. Tee ohjelma. Tehtävänäsi on laskea, kuinka kauan kestää, ennen kuin koko talo (kaikki lattiaruudut) on veden vallassa.
Talon pohjapiirros annetaan samassa muodossa kuin aiemmissa
tehtävissä, mutta merkki
P
tarkoittaa hajonnutta putkea.
Vesi etenee talossa niin, että jos tietyssä lattiaruudussa on vettä,
seuraavalla kierroksella myös kaikissa ruudun naapuriruuduissa on
vettä.
Toteuta metodi
int putkirikko(char[][] talo)
,
joka
laskee montako kierrosta menee kaikkien lattiaruutujen peittymiseen.
Voit olettaa, että hajonneista putkista on yhteys kaikkiin talon lattiaruutuihin.
########## #.#...##.# #.#P#...P# #.....##.# ##########
Kesto: 5
Seuraavaan kuvaan on merkitty ajanhetket, joina vesi saavuttaa talon lattiaruudut yllä olevassa tapauksessa:
########## #5#123##1# #4#P#321P# #32123##1# ##########
Pohjapiirros:
########## #.#...##P# #.#P#P...# #P....##.# ##########
Kesto: 2
Huom!! tässä tehtävässä on tarkoitus tehdä ratkaisu jonka muistinkäyttö on alle O(n^2). Triviaali ratkaisu jossa taulukoidaan kaikki osasummat erikseen ei siis toimi. On olemassa yksinkertainen ratkaisu joka käyttä O(n) muistia.
Tehtävänä on suunnitella yksiulotteisen taulukon tapainen
tietorakenne. Kuitenkin vaatimuksena on, että käytettävissä
on tehokas komento
osasumma(a, b)
,
jolle annetaan
alku- ja loppukohta taulukossa ja joka palauttaa
vastaavien lukujen summan. Komennon aikavaativuuden tulee olla O(1).
Tietorakenteen sisältö ei tule muuttumaan sen luonnin jälkeen.
Tarkastellaan esimerkiksi seuraavaa taulukkoa:
5 | 2 | 6 | 2 | 4 |
Nyt esimerkiksi komento
osasumma(1, 3)
palauttaa taulukon lukujen summan kohdasta 1 kohtaan 3.
Tuloksen tulisi olla 2 + 6 + 2 = 10.
Taulukon indeksointi alkaa siis tässä nollasta.
Tee luokka
Osasumma
,
jolla on:
Osasumma(int[] taulukko)
get(int i)
,
joka palauttaa taulukon
i
:nnen
alkion
osasumma(int a, int b)
,
joka palauttaa
taulukon alkioissa
a,a+1,...,b
olevien arvojen
summan.
int
-lukua
ja että summa
mahtuu
int
-tyyppiseen
muuttujaan.
Huom!
Ohjelmasta tulee aivan liian hidas, jos taulukko
tallennetaan sellaisenaan ja jokainenosasumma
-kutsu käy läpi taulukon annetun kohtien välillä.
Silloinhan aikavaativuus on O(n), mutta vaativuuden tulisi olla
O(1).
Osasumma o = new Osasumma(new int[] {5,2,6,2,4}); System.out.println(o.osasumma(1,3)); System.out.println(o.osasumma(0,1)); System.out.println(o.osasumma(2,2)); System.out.println(o.osasumma(0,4));
Tulostus:
10 7 6 19
Huom! samoin kuin ed. tehtävässä, käytä muistia säästeliäästi, t.s. O(mn), missä m on taulukon korkeus ja n leveys.
Tee luokka
Osasumma
,
jonka toiminta vastaa edellistä
tehtävää, mutta tällä kertaa taulukko on kaksiulotteinen.
Komennon
osasumma
tulee palauttaa minkä tahansa taulukon
osana olevan suorakulmion lukujen summa ajassa O(1). Voit olettaa että
taulukon korkeus ja leveys ovat korkeintaan 1000 ja pyydetyt summat
mahtuvat
int
-tyyppiseen
muuttujaan.
Tarkastellaan esimerkiksi seuraavaa taulukkoa:
3 | 1 | 7 | 6 |
2 | 8 | 5 | 2 |
5 | 2 | 5 | 4 |
Nyt esimerkiksi komento
osasumma(1, 0, 2, 2)
palauttaa
taulukon lukujen summan suorakulmiossa, jonka vasen ylänurkka on
kohdassa (1, 0) ja oikea alanurkka on kohdassa (2, 2). Tuloksen tulisi
olla 2 + 8 + 5 + 5 + 2 + 5 = 27. Tässä taulukon kohdan esitysmuoto on
(y, x), eli ensin tulee rivi ja sitten sarake.
Osasumma o = new Osasumma(new int[][] {{3,1,7,6}, {2,8,5,2}, {5,2,5,4}}); System.out.println(o.osasumma(1,0,2,2)); System.out.println(o.osasumma(1,2,2,3)); System.out.println(o.osasumma(0,2,0,2)); System.out.println(o.osasumma(0,0,2,3));
Tulostus:
27 16 7 50
PS. Osaatko yleistää tekniikan niin, että
osasumma
toimii ajassa O(1) minkä tahansa ulotteisilla taulukoilla?
Tarkastellaan yhtenäistä syklitöntä verkkoa, jossa on n solmua. Verkon solmujen tunnukset ovat 1...n. Tällaiselle verkolle voidaan muodostaa lyhyt kuvaus poistamalla sen solmut tietyssä järjestyksessä.
Ideana on poistaa verkosta yksi kerrallaan solmuja, joiden asteluku on 1 (eli solmusta lähtee vain yksi kaari). Jos vaihtoehtoja on useita, valitaan solmu, jonka tunnus on pienin. Poistamista jatketaan, kunnes verkossa on jäljellä enää yksi solmu. Joka poistamisen yhteydessä merkitään muistiin poistettavan solmun viereisen solmun tunnus, ja tästä muodostuu verkon kuvaus.
Tarkastellaan esimerkiksi seuraavaa verkkoa:
2 3 \ / 5 1 \ / 4
Kuvaus verkon rakenteesta muodostuu seuraavasti:
2 3 3 \ / / 5 1 => 5 1 => 5 1 => 5 => 5 \ / \ / \ / \ 4 4 4 4
Jokaisessa vaiheessa punaisella on merkitty poistettavan solmun
viereinen solmu. Näistä muodostuu verkon kuvaus eli tässä tapauksessa1 1 4 5
.
Tee metodi
HashMap<Integer, HashSet<Integer>> kuvaus(ArrayList<Integer> kuvaus)
jolle annetaan yllä kuvattu kuvaus verkosta ja joka palauttaa
verkon vieruslistaesityksen. Vierulistaesitys tarkoittaa tässä
tapauksessa sitä että kun
kuvaus
palauttaa
HashMapin
hm
,
on
hm.get(i)
solmun
i
naapureitten (tunnisteitten) joukko.
Kuvaus:
1 1 4 5
Vieruslistat:
1: 2 3 4 2: 1 3: 1 4: 1 5 5: 4
Kuvaus:
1 3 3 4 4 7
Vieruslistat:
1: 2 3 2: 1 3: 1 4 5 4: 3 6 7 5: 3 6: 4 7: 4
Hamiltonin kierros on verkossa oleva polku, joka alkaa jostain solmusta, käy kaikissa muissa solmuissa tasan kerran ja palaa sitten takaisin lähtösolmuun.
Tee metodi
boolean hamilton(TreeMap<Integer,
TreeSet<Integer>> verkko)
,
joka tutkii, onko
suuntaamattomassa verkossa Hamiltonin kierrosta. Verkko esitetään
kuten ed. tehtävässä mappina jonka sisällä on settejä. Voit olettaa,
että verkon solmut on numeroitu kokonaisluvuin 0...
n-1
ja
n
on korkeintaan 10.
Tässä verkossa on hamiltonin kierros:
1--2 | | | | 3--0
Hamiltonin kierros on esimerkiksi 1 -> 2 -> 0 -> 3 -> 1.
Tässä verkossa ei ole Hamiltonin kierrosta:
1--2 |\ | \ 3--0
Tee metodi
boolean tietoverkko(boolean[][] verkko)
,
jolle annetaan kuvaus tietoverkosta ja joka tarkistaa, pystyvätkö
kaikki verkon koneet viestimään keskenään. Toisin sanoen ohjelman
tulee tarkistaa, onko verkko yhtenäinen.
Verkossa on n konetta, jotka on numeroitu kokonaisluvuin 0...n-1. Voit olettaa, että n on korkeintaan 1000.
Verkon kuvaus on
vierusmatriisi,
eli kaksiulotteinen
taulukko
verkko
,
jossa
verkko[i][j]
on
true
jos koneitten
i
ja
j
välillä menee yhteys.
Tämän verkon vierusmatriisi:
3 /|\ 4 | 0 \|/ \ 1---2
on:
01110 10111 11000 11001 01010
ja verkko on yhtenäinen.
Tämän verkon vierusmatriisi:
3 /| 4 | 0 \| | 1 2
on:
00100 00011 10000 01001 01010
ja verkko ei ole yhtenäinen.
Opintosuunnittelija Keijo Kojootti suunnittelee kurssien esitietovaatimuksia. Tee hänen avukseen ohjelma, joka tarkastaa, onko esitiedoissa sykli.
Toteuta metodi
public boolean sykli(Esitieto[]
esitiedot)
,
joka palauttaa
true
jos annetuissa
esitiedoissa on sykli. Esitiedot on esitetty
taulukkona
Esitieto
-olioita.
Esitieto
-oliot
toimivat siten, että kurssi
e.esi
tulee suorittaa ennen
kurssia
e.kurssi
.
Seuraavissa esitiedoissa ei ole sykliä:
a b b c a d d c d b c e
Seuraavissa esitiedoissa taas on sykli:
a b b c a d d c d b c a
Tällä kertaa tehtävänäsi on selvittää jokin käypä järjestys
kurssien käymiseen, kun kurssien väliset esitiedot on annettu. Toteuta siis metodi
ArrayList<String> jarjestys(Esitieto[]
esitiedot)
.
Voit olettaa että annetuissa esitiedoissa ei
ole sykliä.
Esitietojen
a b b c a d d c d b c e
eräs käypä suoritusjärjestys on
a d b c e
Esitietojen
ohpe ohja ohja tira tira lama tira tiralabra ohja javalabra javalabra tiralabra ohpe ohma tiralabra ohtuprojekti javalabra ohtuprojekti ohma ohtu ohtu ohtuprojekti lama ohtuprojekti
eräs käypä suoritusjärjestys on
ohpe ohja javalabra tira tiralabra lama ohma ohtu ohtuprojekti
Eulerin kierros verkossa on polku, joka kulkee jokaista kaarta pitkin täsmälleen kerran ja saapuu takaisin lähtöruutuun.
Toteuta metodi
ArrayList<Integer>
eulerinKierros(boolean[][] verkko)
,
joka palauttaa verkon
jonkin Eulerin kierroksen. (Voit olettaa että sellainen on
olemassa.) Syötteenä oleva verkko on esitetty vierusmatriisina --
samoin kuin tehtävässä 1.
Graafi:
0--1 | | | | 3--2
Vierusmatriisi:
0101 1010 0101 1010
Eräs Eulerin kierros:
0,1,2,3
0--1 | | | | 3--2--4 | | | | 5--6
Vierusmatriisi:
0101000 1010000 0101110 1010000 0010001 0010001 0000110
Eräs Eulerin kierros:
0,1,2,5,6,4,2,3
__0_____ / / \ \ 2-3 4 5 6--7 \_____\ /_/ 1
00111100 00001111 10010000 10100000 11000000 11000000 01000001 01000010
Eräs Eulerin kierros:
0,2,3,0,4,1,7,6,1,5
Tehtävänäsi on löytää tehokkain reitti vuoristoisen alueen poikki. Vuoristo esitetään kaksiulotteisena ruudukkona korkeuksia. Ruudusta toiseen kulkeminen on sitä raskaampaa, mitä suurempi niitten välinen korkeusero on. Alueella kulkevan reitin kustannus on siis peräkkäisten ruutujen välisten erotusten itseisarvojen summa
Toteuta metodi
int kiipea(int[][] vuoristo)
,
joka
etsii parhaan (eli kustannukseltaan alhaisimman) reitin
taulukon
vuoristo
ylänurkasta
(vuoristo[0][0]
) alanurkkaan
(vuoristo[n][m]
). Metodin tulee palautta reitin
kustanus.
Voit olettaa että syötten koko on korkeintaan
40x40
Vihje! Käytä joko Bellman-Fordia (luentokalvojen s. 504) tai Dijkstran algoritmia (luentokalvojen s. 510).
Vihje!
Jos valitset Dijkstran, toteutuksessa kannattaa
käyttää javan
PriorityQueue
-luokkaa.
Joudut
todennäköisesti toteuttamaan
jonkinnäköisen
Paikka
-luokan,
jonka olioita
laitat
PriorityQueue
en.
Paikka
-olioille
kannattaa tehdä
Comparator
,
joka
annetaan
PriorityQueue
lle.
Tässä eräs vuoristo ja lyhin reitti siinä.
1 2 3 4 5 2 1 3 3 6 3 3 9 3 9 4 3 2 4 7 5 7 5 9 6
Reitin kustannus on 1+1+0+0+0+1+3+1=7.
Haluat tehdä voittoa valuuttamarkkinoilla ja suunnittelet tätä varten tietokone-ohjelmistoa. Ohjelman erään komponentin tulee löytää paras sarja valuutanvaihtoja jolla päästään annetusta valuutasta toiseen annettuun valuuttaan.
Saat syötteenäsi
n
eri valuutan väliset
vaihtokurssit kaksiulotteisena taulukkona
double[][]
kurssit
sekä kaksi valuuttaa
s
ja
t
.
Tehtävänäsi on löytää sarja
vaihtoja
v
:
s -> v[0] -> v[1] -> ... -> v[n] -> t
Siten, että vaihdon arvo, eli tulo
kurssit[v[0]][s] * kurssit[v[1]][v[0]] * ... * kurssit[t][v[n]]
maksimoituu.
Toteuta siis metodi
ArrayList<Integer> vaihda(double[][] kurssit, int s, int t)
.
Huom!
kurssit[i][i]
on aina
1.0
Huom! voit olettaa ettei verkosta löydy "positiivista sykliä", ts. vaihtosarjaa jota toistamalla voit saada mielivaltaisen paljon rahaa.
Jos vaihtotaulukko on:
X 2.1 3.0 0.4 X 1.5 0.3 0.6 X
(Eli valuuttaa 0 voidaan vaihtaa valuutaksi 2 kertoimella 0.3 jne.) Paras tapa vaihtaa valuuttaa 0 valuutaksi 2 on 0->2 (kerroin 0.3) ja paras tapa vaihtaa valuuttaa 2 valuutaksi 0 on 2->1->0 (kerroin 1.5*2.1 = 3.15).
Tehtäväpohjassa.
Alkuluku on kokonaisluku (2 tai suurempi), joka on jaollinen vain luvulla 1 ja itsellään. Esimerkiksi luvut 5, 13 ja 31 ovat alkulukuja. Luku 14 ei ole alkuluku, koska se on jaollinen 2:lla ja 7:llä.
Tee ohjelma SeuraavaAlkuluku.java
,
jolle annetaan kokonaisluku ja joka tulostaa
seuraavan tätä lukua suuremman alkuluvun.
Ohjelmasi voi olettaa, että annettu
luku on int
-tyyppinen
ja korkeintaan miljoona.
Anna luku: 10 Seuraava alkuluku: 11
Anna luku: 123 Seuraava alkuluku: 127
Alkuluku on kokonaisluku (2 tai suurempi), joka on jaollinen vain luvulla 1 ja itsellään. Esimerkiksi luvut 5, 13 ja 31 ovat alkulukuja. Luku 14 ei ole alkuluku, koska se on jaollinen 2:lla ja 7:llä.
Tee ohjelma
SeuraavaAlkuluku.java
,
jolle annetaan kokonaisluku ja joka tulostaa
seuraavan tätä lukua suuremman alkuluvun.
Ohjelmasi voi olettaa, että annettu
luku on
int
-tyyppinen
ja korkeintaan miljoona.
Anna luku: 10 Seuraava alkuluku: 11
Anna luku: 123 Seuraava alkuluku: 127
Saat kuvauksen pienen itäeurooppalaisen maan tieverkostosta ja tehtävänäsi on tuottaa kaikkien kaupunkien väliset lyhimmät etäisyydet.
Tieverkosto kuvataan kaksiulotteisena talukkona
int[][]
tiet
,
jossa
tiet[i][j] = 0
jos kaupunkien
nro
i
ja
j
välillä ei ole tietä,
ja
tiet[i][j] = x
kun kaupunkien välillä on tie jonka
pituus on
x
kilometria.
Sinun tulee tuottaa kaksiulotteinen
taulukko
etaisyydet
,
jossa
etaisyydet[i][j]
on kaupunkien
i
ja
j
välisen lyhimmän reitin pituus, tai
0
jos kaupunkien välillä ei ole reittiä.
Syöte:
0 1 0 1 0 2 0 2 0
Tuloste:
0 1 3 1 0 2 3 2 0
Tämä vastaa seuraavaa verkkoa:
1km 2km 0 ----- 1 ----- 2
Syöte:
0 1 0 0 1 0 0 0 0 0 0 2 0 0 2 0
Tuloste:
0 1 0 0 1 0 0 0 0 0 0 2 0 0 2 0
Tämä vastaa seuraavaa verkkoa:
1km 2km 0 ----- 1 2 ----- 3
Syöte:
0 1 7 0 0 1 0 0 4 2 7 0 0 1 0 0 4 1 0 1 0 2 0 1 0
Tuloste:
0 1 5 4 3 1 0 4 3 2 5 4 0 1 2 4 3 1 0 1 3 2 2 1 0
Tämä vastaa seuraavaa verkkoa:
1km 2km 0 ------ 1 ----- 4 \ \ / 7km \ 4km \ / 1km \ \ / 2 ------ 3 1km
Tässä tehtävässä laajennetaan edellistä siten, että myös kaupunkien väliset lyhimmät reitit saadaan laskettua.
Toteuta luokka
Tieverkosto
,
jolla on:
Tieverkosto(int[][] tiet)
int etaisyys(int i, int j)
,
joka palauttaa
lyhimmän etäisyyden kaupunkien
i
ja
j
välillä.
ArrayList<Integer> reitti(int i, int
j)
,
joka palauttaa lyhimmän reitin kaupunkien
i
ja
j
välillä
Metodien
etaisyys
ja
reitti
tulisi
toimia nopeasti. Laske kaikki tarvittava informaatio
konstruktorissa.
Vihje älä kuitenkaan luo arrayListejä konstruktorissa - vaan laske kaikki niiden nopeaan luontiin vaadittava konstruktorissa.
Edellisen tehtävän esimerkin verkolle:
1km 2km 0 ------ 1 ----- 4 \ \ / 7km \ 4km \ / 1km \ \ / 2 ------ 3 1km
Lyhin reitti solmusta 0 solmuun 2 on
0, 1, 4, 3, 2
Kaupoissa on ollut jo muutaman vuoden robotti-imureita, jotka imuroivat huoneiston automaattisesti. Tarkastellaan seuraavaa yksinkertaista robotti-imurin tehtävää
Imuroitavan huoneiston pohjapiirros esitetään ruukukkona
.E.. .S.. ..S. .E..
Jossa
Robotissa on näet akku, jonka kapasiteetti on A energiayksikköä. Robotti voi siirtyä nykyisestä ruudustaan naapuriruutuihin, ei kuitenkaan kulmittain. Jokainen siirtymä kuluttaa akusta yhden energiayksikön. Jos akku pääsee tyhjentymään, sammuu robotti, eikä se enää pysty jatkamaan siivoamista.
Toteuta luokkaan
RobottiImuri
metodi
public boolean loytyykoReitti(int x, int y)
, joka tarkastaa, onko robotin mahdollista imuroida koko huoneisto, siten että robotti aloittaa liikkumisen parametrinä annetusta ruudusta, jossa on sähköpistoke, kiertää koko huoneiston ja palaa takaisin lähtöruutuun. Metodi palauttaa true, mikäli tällainen reitti löytyy.
Robotti saa konstruktoriinsa parametrina Huoneiston pohjapiirroksen ja akkukeston.