Utolsó módosítás: 2008. november 13.
Ez a feladatsor az első verseny első, november 13. csütörtök reggeli turnusához tartozik.
Le lehet tölteni az összes példa be- és kimenetet és példa forráskódot egyben: z0p.tgz.
A program bemenete egy sorból áll, amiben tizennégy szám áll szóközzel elválasztva. Mindegyik szám 0 és 1000 közti egész. A kimenetben ugyanennyi számot kell kiírni. Ahol a bemenetben pozitív szám áll, ott a kimenetben a megfelelő szám ugyanaz. Ahol viszont a bemenetben nulla áll, ott ezt a kimenetben az ez előtti utolsó nem nulla számmal kell helyettesíteni.
(A bemenetet a standard bemenetről olvassa, a kimenetet a standard kimenetre írja. A kimenetben a számokat a legrövidebb decimális alakban kell kiírni, közöttük egy szóköz áll, de a sor végén nincs szóköz; a sort újsor jellel kell lezárni.)
Például ha a bemenet a következő:
716 102 0 0 682 314 76 319 460 0 762 0 278 748
akkor a kimenet:
716 102 102 102 682 314 76 319 460 460 762 762 278 748
Még néhány példa.
Ez a feladat az 1. mintafeladatsor 6. feladatának (körlevél írása) módosítása. A feladat eléggé hasonlít, így csak a különbségeket sorolom fel. A megoldáshoz érdemes a mintafeladat s1f6.c mintamegoldásból kiindulni.
A feladat abban különbözik az eredetitől, hogy a sablonban van egy új fajta jelölő, amihez egy több sorból álló mező tartozik.
A sablonban kétféle mezőjelzés van: közönséges, és hosszú jelzés.
A közönséges jelzés ugyanúgy néz ki, mint eddig: @i
, és legfeljebb 50 van belőle.
A hosszú jelzés három karakterből áll: ezek a @m
majd egy újsor jel.
Hosszú jelzésből pontosan egy van (ez az 50 közönséges jelzésbe nem számít bele),
és ez mindig sor elején van.
A rekordok megnyitó jelzése egy sor, amiben egy pozitív szám áll, de ez a szám az eredeti feladattal ellentétben nem feltétlenül egy, hanem 1-től 50-ig bármi lehet. Ez a szám adja meg, hány sorból áll a hosszú jelzéshez tartozó mező. A rekordban ez után minden mező jelzéshez egy mező érték tartozik. A közönséges jelölőkhöz tartozó jelölő továbbra is egy sor, és ennek a végén álló újsor jelet továbbra sem kell behelyettesíteni a levélbe. A hosszú jelölőhöz azonban egy több sorból (legalább egy sorból) álló mező tartozik, ennek a sorainak a számát a rekord megnyitó jelzése adja meg. A hosszú jelölő értékéhez hozzá tartozik minden sorának a végén álló újsor jel, ezzel együtt kell tehát a mezőt a jelölő helyére behelyettesíteni. A mezők olyan sorrendben vannak felsorolva a rekordban, ahogy a hozzájuk tartozó jelölők a mintában elhelyezkednek.
A levelet továbbra is úgy kapjuk, hogy a sablonban a jelölőket a mezők értékére kicseréljük.
Íme néhány letölthető példa. (A 0. példát ajánlom megnézni, mert ez olvashatóbb a többinél.)
Végül egy tanács: a rekord kezdősorából a számot a
sscanf(line, "%d", &i)
hívással olvassuk be
az int
típusú i
változóba.
Ebben a feladatban a programnak egy egyszerű mintát kell eltolással vagy tükrözéssel ismételnie, hogy egy nagyobb mintát kapjunk. Először egy egyszerűbb változtatot mutatok be megoldással, utána elmondom a bonyolultabb feladatot, amit ez alapján meg kell oldaniuk.
Az egyszerűbb változatban a bemenet első sora négy egész számot tartalmaz, ezek H, W, K, M. Az első szám 0 <= H <= 20 a kis minta sorainak számát adja meg, a második 0 <= W <= 20 a kis minta oszlopainak a számát, a harmadik 0 <= K <= 10 a vízszintesen egymás mellé rakott példányok számát, a negyedik 0 <= M <= 2 az ismétlés módját. Ez után a bemenetnek még H sora van, mindegyik sor W nyomtatható ascii karakterből (ideértve a szóközt is) áll, majd egy újsor jelből áll. Ezek a sorok meghatározzák a minta egy elemi celláját, ami téglalap alakú. Ezt a téglalapot kell egymás mellett vízszintesen K-szor megismételni, hogy a kimenetet, a nagy mintát megkapjuk.
Ha M = 0, akkor az ismétlések egyszerűen egymás eltolt példányai, a következő mindig W karakterrel az előzőtől jobbra. Ha M = 1, akkor minden második ismétlés vízszintesen tükrözve van, így minden példányt úgy kapunk, hogy az előzőt ennek jobb szélére tükrözünk. Végül ha M = 1, akkor szintén tükrözéssel kapjuk a példányokat, de a vízszintesen tükrözött példányoknak el kell hagyni a bal szélső oszlopát, ezt az oszlopot az előző példány jobb szélső oszlopa úgymond eltakarja.
Ha egy karaktert vízszintesen tükrözünk,
akkor a következő négy pár karakter egymásra cserélődik ki:
() [] <> /\
, a többi karakter pedig nem változik.
Nézzünk egy-egy példát mindegyik ismétlési módra! Az egyik bemenet.
3 10 6 0 ____(D) /( _ )\, []] []]az ehhez tartozó kimenet pedig a következő.
____(D) ____(D) ____(D) ____(D) ____(D) ____(D) /( _ )\,/( _ )\,/( _ )\,/( _ )\,/( _ )\,/( _ )\, []] []] []] []] []] []] []] []] []] []] []] []]A második esetben a bemenet
5 5 8 1 \ _ \ ( \ ( \ ( | |ehhez pedig a következő kimenet tartozik.
\ __ /\ __ /\ __ /\ __ / \ ( ) / \ ( ) / \ ( ) / \ ( ) / \ ( ) / \ ( ) / \ ( ) / \ ( ) / \ ( ) / \ ( ) / \ ( ) / \ ( ) / | || || || || || || || |Végül a harmadik tükrözési módot ez a bemenet mutatja be.
4 3 6 2 O -+ | /Ebből ez a kimenet lesz.
O O O -+- -+- -+- | | | / \ / \ / \
Itt van ez a három példa is, és még három másik.
Nézzük ennek a mintamegoldását, ami egyben letölthető innen: z0f6.c.
#include#include #include void die(char *msg) { fprintf(stderr, "%s\n", msg); exit(1); } char mirror_char(char *key, char c) { while (key[0] && key[1]) { if (c == key[0]) return key[1]; if (c == key[1]) return key[0]; key += 2; } return c; }
A fenti mirror_char függvény egyetlen c
karaktert tükröz,
ha a key
kulcs egy páros hosszú nullára végződő sztring,
ami a tükörkép karakterpárokat tartalmazza.
#define TEMPLATE_MAXHEIGHT 20 #define TEMPLATE_MAXWIDTH 20 int template_height, template_width; char template[TEMPLATE_MAXHEIGHT][TEMPLATE_MAXWIDTH]; int num_copies, copy_mode; char mirrored[TEMPLATE_MAXHEIGHT][TEMPLATE_MAXWIDTH]; void read_template(void) { char line[TEMPLATE_MAXWIDTH + 5]; int r; for (r = 0; r < template_height; r++) { if (!fgets(line, TEMPLATE_MAXWIDTH + 5, stdin)) die("eof or error reading template line"); if (template_width != strlen(line) - 1) die("template line length wrong"); memcpy(template[r], line, template_width); } }
A négy paramétert (H, W, K, M) rendre négy változóban tároljuk:
template_height, template_width, num_copies, copy_more
.
A read_template függvényt már ezek kitöltése után kell meghívni
(no meg miután az első sort a bemenetről beolvastuk):
ez a template mátrixba beolvassa az elemi cella karaktereit
(de újsorokat vagy nullákat nem tárol el).
void mirror_template(void) { int r, c; for (r = 0; r < template_height; r++) { for (c = 0; c < template_width; c++) mirrored[r][c] = mirror_char("()<>[]\\/", template[r][template_width - c - 1]); } }
A mirror_template
függvény elkészíti az elemi cella vízszintes tükörképét a
mirrored
mátrixba.
void output(void) { int row, copy; char *p; int l; for (row = 0; row < template_height; row++) { for (copy = 0; copy < num_copies; copy++) { if (0 == copy % 2 || 0 == copy_mode) { p = template[row]; l = template_width; } else if (2 == copy_mode) { p = mirrored[row] + 1; l = template_width - 1; } else { p = mirrored[row]; l = template_width; } fwrite(p, l, 1, stdout); } putchar('\n'); } } int main(void) { char line[500]; if (!fgets(line, sizeof(line), stdin)) die("error reading parameters"); if (4 != sscanf(line, "%d%d%d%d", &template_height, &template_width, &num_copies, ©_mode )) die("error parsing parameters"); if ( TEMPLATE_MAXHEIGHT < template_height || template_height < 0 || TEMPLATE_MAXWIDTH < template_width || template_width < 0 || 2 < copy_mode || copy_mode < 0 ) die("invalid parameters"); read_template(); mirror_template(); output(); return 0; }
Végül a
Az ön feladata ettől abban különbözik, hogy a mintát nem csak oldalra,
hanem lefele is mozgatni kell.
Az igazi feladatban tehát a bemenet első sora hat egész paraméterből áll szóközzel elválasztva:
ezek a H, W, K, M, K', M'.
Az első négy paraméter jelentése megegyezik az előzőekkel.
Miután az elemi cellát vízszintesen egymás mellett az előző megismételtük,
a kapott széles mintát függőlegesen egymás alá is meg kell ismételni K'-ször.
A felső példány az eredeti, a többi példány készítésének módját az M' szabályozza hasonlóan,
mint ahogy az M a vízszintes másolást irányítja.
Ha M' = 0, akkor az egymás alatti példányok egymás eltoltjai H soronként.
Ha M' = 1, akkor minden második példány függőlegesen tükrözve van,
a tükörtengely az előző széles minta alján helyezkedik el.
Ha M' = 2, akkor a tükörtengely az előző széles minta alsó sorának közepe,
így a rendesen álló példányok alsó sora átfedi a függőlegesen tükrözött példányok felső sorát,
ez utóbbi sort tehát nem írjuk ki.
Amikor egy karaktert függőlegesen tükrözünk,
akkor a következő két pár cserélődik ki egymásra:
Íme néhány példa.
A bonyolultabb feladatot kell tehát megoldania,
ehhez felhasználhatja az egyszerűbbre adott mintamegoldást.
A módosított forrást a SIO rendszerben a z0f2 feladathoz kell feltölteni.
Ebben a feladatban algebrai kifejezéseket fogunk átalakítani
prefix írásmódról hagyományos síkbeli matematikai jelölésre.
Algebrai kifejezésnek tekintjük a következőket:
változó, kis szám konstans,
két algebrai kifejezés hatványozása, hányadosa, szorzata, különbsége vagy összege.
A prefix írásmódban az operátor jelét az argumentumai elé írjuk.
Ennek az az előnye, hogy a jelölés zárójelek nélkül is egyértelmű.
Változónak tekintjük az angol ábécé kisbetűit,
ezek prefix jelölésben is egyszerűen egy kisbetűbel vannak megadva.
Hasonlóan a 0 és 9 közti számokat kis számkonstansnak tekintjük,
ezeket a prefix kifejezésben egy számjegy jelzi.
(Nagyobb számot nem írunk le.)
Ha x és y két algebrai kifejezés, prefix alakjuk
Az algebrai kifejezéseknek definiáljuk a hagyományos jelölését is.
Ez egy karakterekből álló táblázat, aminek a magasságán és szélességén és a tartalmán kívül
még ki van jelölve az egyik sora alapsornak.
A változók és konstansok hagyományos írása egy 1 széles 1 magas táblázat (az alapsor egyértelmű),
aminek egyetlen karaktere a megfelelő betű illetve számjegy.
Bonyolultabb kifejezések hagyományos alakját kisebb táblázatok összerakásával kapjuk meg.
Ilyenkor az új táblázat pont akkora, hogy az összes összerakott táblázat éppen beleférjen.
Ahova semmilyen táblázat nem kerül, ott szóközök állnak.
Hatványozásnál az alap és a kitevő hagyományos alakát úgy rakjuk össze,
hogy a kitevőhöz tartozó táblázat bal alsó sarka épp összeérjen az alaphoz tartozó táblázat
jobb felső sarkával.
A hatvány hagyományos alakjának az alapsora az alap alapsora.
Ha egy algebrai kifejezés két részkifejezés hányadosa,
akkor a két részkifejezés hagyományos alakja egymás alá kerül úgy, hogy egy sor kimarad köztük.
Vízszintesen úgy kell igazítani a két résztáblázatot, hogy a közepük egymás fölött legyen,
vagy, ha ez paritási okból lehetetlen, akkor a szélesebb táblázat közepe 1/2 karakterrel
a keskenyebb közepe alatt legyen.
A hányados táblázata jobb és bal oldalt is egy oszloppal szélesebb,
mint a két rész közül a szélesebbik.
Az alapsor az a sor, amelyik a két részkifejezés között szabadon maradt,
ezt a sort a táblázatban végig mínusz [
Ha egy algebrai kifejezés két részkifejezés összege, különbsége, vagy szorzata,
akkor a két részkifejezés hagyományos alakját úgy kell összerakni,
hogy a két rész alapsora ugyanabba a sorba essen, ez lesz az új kifejezés alapsora.
Vízszintesen a két résztáblázat között
szorzat esetén egy, összeg vagy különbség esetén három oszlopot ki kell hagyni.
A szorzat esetén nincs műveleti jel;
összegnél és különbségnél a három kihagyott oszlop közül a középsőnek
és az alapsornak a metszetébe egy plusz [
Mielőtt azonban összeraknánk a kifejezéseket, néha még zárójeleket kell beszúrni.
Egy hagyományos alakot úgy kell bezárójelezni, hogy a táblázatot bal és jobb oldalt
egy-egy oszloppal kibővítjük, ezeket a táblázat teljes magasságában bal [
Pontosan a következő esetekben kell zárójelezni.
Zárójelezni kell egy szorzatot, hatványt, vagy hányadost, ha egy hatvány alapjában áll;
zárójelezni kell egy összeget vagy különbséget,
ha ez egy szorzat tényezője vagy egy hatvány alapja.
Olyan kifejezésekkel nem foglalkozunk, amiben egy szorzat jobb oldali tényezője szorzat,
vagy ahol egy összeg vagy különbség jobb oldali tagja összeg vagy különbség.
Amikor kiírunk egy kifejezést hagyományos alakban, akkor a táblázatot soronként kell kiírni,
a sorok végéről a szóközöket elhagyva, és minden sor után újsor jelet kell írni.
Végül az egész kifejezés után még egy újsort ki kell írni.
Lássunk egy példát. A következő kifejezést nézzük.
(a/b)1/2 - (c + d)(1 + a) - (a - d)/b2 + b2 / (ac)
Ennek a prefix alakja a következő.
A hagyományos alakja egész kifejezésként kiírva pedig az alábbi.
A feladatban a bemenet több sorból áll, minden sorban egy algebrai kifejezés prefix alakja van.
A kimenetben ezeknek a kifejezéseknek a hagyományos alakját kell fölrajzolni.
Minden kifejezés olyan, hogy a prefix alakja legfeljebb 100 karakterből áll.
Néhány példa a következő.
Ezt az egyszerűbb feladatot a z0f7.c program oldja meg.
Íme a forráskód.
A kód megértéséhez a következőt kell tudni.
Minden kifejezést két lépésben dolgozunk fel.
Az első lépésben, amit a
A második fázisban a leírókat bejárva kirajzoljuk a kifejezéseket.
A paper karakter-mátrixba kerül az eredmény: ennek elég nagy részét kitöltjük szóközökkel,
majd rekurzívan hívjuk a
Mármost önnek azzal kell bővíteni a programot, hogy faktoriálist is kezeljen.
Az új szabályok a következők.
Ha x algebrai kifejezés, amelynek
Példák.
A módosított feladat megoldását a SIO-ra a z0f3 feladathoz töltse föl.
main függvény beolvassa a négy paramétert,
meghívja az első két segédfüggvényt, majd
meghívja az output függvényt, ami soronként végigmenve kiírja a kimenetet.
,' /\
.
A többi karakter nem változik.
3. feladat (z0f3): algebrai kifejezések kirajzolása
X
és Y
,
akkor értelmes kifejezést kapunk, ha az x-et az y-ik hatványra emeljük,
ennek az xy hatványnak a prefix alakja ^XY
.
Hasonlóan az x/y hányados, az x*y szorzat, az x-y különbség és az x+y összeg
prefix alakje rendre /XY
, *XY
, -XY
, +XY
.
-
] jelekkel kell feltölteni.
+
] illetve mínusz [-
] karaktert
kell elhelyezni.
(
]
illetve jobb [)
] zárójelekkel kell kitölteni.
Az új táblázat alapsora ugyanaz, ahova a kisebb táblázat alapsora került.
Egy kifejezés hagyományos alakját bizonyos esetekben ennek bezárójelezésével kell helyettesíteni,
majd az eredeti táblázat helyett ezt használni nagyobb kifejezés építéséhez.
+--^/ab/12*+cd+1a/-ad^b2/^b2*ac
1
---
2 2
( a ) a - d b
(---) - (c + d) (1 + a) - ------- + -----
( b ) 2 a c
b
#include
layout
függvény hajt végre,
értelmezzük a prefix kifejezést, és minden részkifejezéséhez csinálunk egy
struct plan
típusú leírót.
A leíróban eltárolunk elég információt ahhoz, hogy a kifejezés visszaállítható legyen,
erre szolgálnak a head, lhs, rhs
mezők;
valamint az ehhez tartozó hagyományos kifejezés méretét is eltároljuk:
a width
mező a tábla szélességét tartalmazza,
a height
mező a magasságot az alapsortól a táblázat tetejéig,
a depth
pedig a mélységet az alapsor alatt.
A prec
mezőt arra használjuk, hogy eldöntsük,
kell-e zárójelezni a kifejezést,
ha igen, akkor a zárójelezett változathoz egy újabb leírót hozunk létre.
paint
eljárást,
ami egy leírót és a részkifejezés alapsorának a bal szélső mezőjének pozícióját kapja
argumentumként,
és a papar
mátrixra rajzol.
Végül a mátrix alapján kiírjuk az eredményt.
X
a prefix alakja,
akkor x! (azaz x faktoriális) is algebrai kifejezés,
és ennek prefix alakja !X
.
Ha az x részkifejezésnek megtaláltuk a hagyományos alakját,
akkor az x! hagyományos alakját úgy kapjuk, hogy az előbbihez hozzárakunk még egy oszlopot jobbra,
és ebbe az oszlopba az alapsorba (ami a részkifejezésnek és a teljesnek is az alapsora),
rakunk egy felkiáltójel [!
] karaktert.
Előbb azonban a részkifejezéshez tartozó táblázatot zárójelbe kell rakni,
ha ez a részkifejezés hatvány, hányados, szorzat, összeg, vagy különbség.
A teljes faktoriálist sosem kell zárójelbe rakni.