Prog 2 1. verseny feladatsor

Utolsó módosítás: 2008. november 13.

Ez a feladatsor az első verseny második, november 13. csütörtök délutáni turnusához tartozik.

Le lehet tölteni az összes példa be- és kimenetet és példa forráskódot egyben: z1p.tgz vagy z1p.zip.

0. feladat (z1f0): felváltott előjelű összeg

A program bemenete egy sorból áll, amiben tizennégy szám áll szóközzel elválasztva: A_0, …, A_13. Mindegyik szám 0 és 1000 közti egész. A kimenet egyetlen sorába egy számot kell beírni: ezen számok felváltott előjelű összegét, vagyis az A_0 - (A_1 - (A_2 - … - (A_12 - A_13)…)) számot.

(A bemenetet a standard bemenetről olvassa, a kimenetet a standard kimenetre írja. A kimenetben a számot a legrövidebb decimális alakban kell kiírni, a sort újsor jellel kell lezárni.)

Például ha a bemenet a következő:

965 278 60 294 990 801 336 905 523 58 169 325 662 139

akkor a kimenet:

905

Még néhány példa.

z1f0be0 -> z1f0ki0

z1f0be1 -> z1f0ki1

z1f0be2 -> z1f0ki2

z1f0be3 -> z1f0ki3

z1f0be4 -> z1f0ki4

1. feladat (z1f1): körlevél több változattal

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 körlevél sablonban van egy változó rész, aminek több változata van, majd minden rekord ezen változatok közül egyet választ ki.

Pontosan a következők az új szabályok. A sablonban négy féle jelölő van. A mező jelölő továbbra is a @i, ez a rekord következő mezőjének értékét másolja be a levélbe. A sablonban ezen kívül van valahol pontosan egy @s jelölő, és később pontosan egy @e jelölő, és ezek között néhány @c jelölő. A sablonban a @s jelölőtől a @e jelölőig terjedő szakasz a változó rész, ezt a @c jelölő változatokra osztja. Így eggyel több változat van, mint ahány @c jelölő van, ezeket egytől folyamatosan számozzuk. Mező jelölő és közönséges szöveg is lehet a változatokon belül is, de a változó rész előtt és után is.

A rekordok fejléce egy sor, amiben egy pozitív egész szám áll (meg egy újsor jel), de ez az egész szám most nem csak egy lehet, hanem nagyobb is. Ez az egész szám adja meg a használandó változat sorszámát. A sablonból és a rekordból úgy kapjuk a levelet, hogy a változó részből csak a fejlécnek megfelelő sorszámú egyetlen változatot tartjuk meg. A többi változatot eldobjuk: a levélbe nem kerül be a bennük lévő szöveg és a bennük lévő mezők. Sőt, a rekord csak azokhoz a mező jelölőkhöz ad meg mezőt, amik a kiválasztott változatban, vagy a változó részen kívül vannak. (A mezők a rekordban olyan sorrendben szerepelnek, ahogyan a mező jelölők a sablonban. A levélbe nem írjuk be a @s, @c, @e jelölőket sem.)

A sablonban összesen legfeljebb 50 darab @i mező jelölő lehet, és legfeljebb 20 változat. A sablon a jelölőkkel együtt továbbra is legfeljebb 10000 karakter hosszú.

Lássunk egy példát, hogy mindez világos legyen. A bemenet legyen a következő.

Tisztelt @i úr/hölgy!
Az ön által benyújtott 501-A típusú űrlapon 
benyújtott méltányossági kérvényt elutasítottuk@s
szabályszerű mellékletek csatolásának elmulasztása@c
ugyanazon félévben korábban megitélt méltányosság@c
a 2008 évben elfogadott sóhivatali szabályzat 
15. paragrafus @i pontjának meg nem felelés @e
miatt.  Ezen határozat ellen fellebbezhet a határozat
kézhez kapása utáni három napon belül az 504 típusú
űrlap személyes benyújtásával.
Központi Sóhivatal, @i

$
1
Kovács Szilvia
2008. december 4.
3
Göndör Elemér
4.
2008. december 6.
3
Kis Ágnes
2. (a)
2008. december 9.
2
Alacsony Jenő
2008. december 10.
0

Vegyük észre, hogy a változó rész előtt és után egy-egy mező jelölő van, ezért a nevet és a dátumot mindegyik rekord megadja. Ráadásul a harmadik változat tartalmaz még egy mező jelölőt, ezért azok a rekordok, amik a harmadik változatot kérik, a kettő között még egy mezőt megadnak, a cikkely sorszámát.

A kimenet a következő.

Tisztelt Kovács Szilvia úr/hölgy!
Az ön által benyújtott 501-A típusú űrlapon 
benyújtott méltányossági kérvényt elutasítottuk
szabályszerű mellékletek csatolásának elmulasztása
miatt.  Ezen határozat ellen fellebbezhet a határozat
kézhez kapása utáni három napon belül az 504 típusú
űrlap személyes benyújtásával.
Központi Sóhivatal, 2008. december 4.

Tisztelt Göndör Elemér úr/hölgy!
Az ön által benyújtott 501-A típusú űrlapon 
benyújtott méltányossági kérvényt elutasítottuk
a 2008 évben elfogadott sóhivatali szabályzat 
15. paragrafus 4. pontjának meg nem felelés 
miatt.  Ezen határozat ellen fellebbezhet a határozat
kézhez kapása utáni három napon belül az 504 típusú
űrlap személyes benyújtásával.
Központi Sóhivatal, 2008. december 6.

Tisztelt Kis Ágnes úr/hölgy!
Az ön által benyújtott 501-A típusú űrlapon 
benyújtott méltányossági kérvényt elutasítottuk
a 2008 évben elfogadott sóhivatali szabályzat 
15. paragrafus 2. (a) pontjának meg nem felelés 
miatt.  Ezen határozat ellen fellebbezhet a határozat
kézhez kapása utáni három napon belül az 504 típusú
űrlap személyes benyújtásával.
Központi Sóhivatal, 2008. december 9.

Tisztelt Alacsony Jenő úr/hölgy!
Az ön által benyújtott 501-A típusú űrlapon 
benyújtott méltányossági kérvényt elutasítottuk
ugyanazon félévben korábban megitélt méltányosság
miatt.  Ezen határozat ellen fellebbezhet a határozat
kézhez kapása utáni három napon belül az 504 típusú
űrlap személyes benyújtásával.
Központi Sóhivatal, 2008. december 10.

A többi példa a következő.

z1f1be0 -> z1f1ki0

z1f1be1 -> z1f1ki1

z1f1be2 -> z1f1ki2

z1f1be3 -> z1f1ki3

z1f1be4 -> z1f1ki4

z1f1be5 -> z1f1ki5

2. feladat (z1f2): szobafoglalás

Ön egy kicsi de népszerű túristaszállást vezet, és fogadnia kell a bejövő telefonhívásokat. A telefonban többnyire előre szeretnének helyet foglalni a szállásra. Az ön feladata eldönteni, van-e elég szabad szoba ahhoz, hogy egy foglalást elfogadjon, vagy el kell utasítani.

Pontosabban a feladat a következő. A bemenet megadja a szálláson lévő összes kiadható szobák számát, és a telefonos foglalások adatait. A telefonos foglalásoknak sorrendje van, és ebben a sorrendben is kell kiszolgálni őket, vagyis minden híváskor azonnal el kell dönteni, hogy a kért foglaláshoz van-e elég szabad szoba. Ha az derül ki, hogy van elég szabad szoba, akkor ezeket azonnal le is kell foglalni a megadott időtartamra, és későbbi telefonhívásoknál már ezt is figyelembe kell venni.

Az összes foglalás teljesen egy 60 éjszakát felölelő időszakba esik. Az éjszakákat dátum helyett egyszerűen egy sorszámmal jellemezzük, ami 0 és 59 közé esik. Például jelölheti 0 a 2008 november 18-ra virradó éjszakát, 1 a következő éjszakát, stb, 59 a 2009 január 16-ra virradó éjszakát.

Minden foglalásnak három paramétere van: az első éjszaka sorszáma, az utolsó éjszaka sorszáma, és az igényelt szobák száma. Ha az első két szám megegyezik, akkor a vendégek egy éjszakát aludnának a szálláson. A foglalást pontosan akkor kell elfogadni, ha ezzel és a korábbi sikeres foglalásokkal együtt semelyik éjjel sem foglaltak több szobát, mint ahány kiadható szoba a szálláson van. Egy foglalást nem lehet csak részben elfogadni: ha a társaságból csak néhányan férnének el a szálláson, akkor inkább mindenki másik szállást keres.

A program bemenetének első sorában két egész szám áll, az első a kiadható szobák száma, a második a foglalások száma (mindkét szám legfeljebb 100). Ez után még annyi sora van a bemenetnek, amekkora a második szám az első sorban. Minden iyen sorban három egész szám áll: az első éjszaka sorszáma, az utolsó éjszaka sorszáma, és az igényelt szobák száma. (Az első éjszaka sorszáma nem lehet nagyobb az utolsó éjszaka sorszámánál, mindkettő legalább 0 és legfeljebb 59, a szobák száma legalább 0 de legfeljebb 100.) A foglalások a hívások sorrendjében vannak felsorolva, tehát az elsőre kell először válaszolni.

A program kimenete annyi sorból áll, ahány foglalás van. Minden sorba egyszerűen egy 1-es számjegyet kell írni, ha a foglalás sikeres, vagy egy 0-ás számjegyet, ha a foglalást elutasította. A sorok végén újsor jel áll.

Íme egy egyszerű példa bemenet.

2 8
3 8 1
6 14 1
6 6 1
4 4 1
0 4 1
0 3 1
10 10 2
9 13 1
Az ehhez tartozó kimenet a következő.
1
1
0
1
0
1
0
1

A harmadik foglalást elutasítottuk, mert a 6. éjszakára már az első két telefonáló lefoglalt egy-egy szobát, és csak két szoba van. Az ötödik foglalást azért utasítjuk el, mert a 4. éjszakára az első és a negyedik telefonáló foglalt egy-egy szobát. A hetedik foglalást azért utasítjuk el, mert a telefonáló mindkét szobát szeretné megkapni, de csak egy van szabad a 10. éjszaka. Mivel ezt teljesen el kell utasítani, a 10. éjszaka még egy szoba szabad marad, aminek az utolsó telefonáló nagyon örül.

Még pár bemenet és kimenet a következő.

z1f6be0 -> z1f6ki0

z1f6be1 -> z1f6ki1

z1f6be2 -> z1f6ki2

z1f6be3 -> z1f6ki3

z1f6be4 -> z1f6ki4

z1f6be5 -> z1f6ki5

Íme a mintaprogram, ami megoldja ezt a feladatot. A programot letöltheti a z1f6.c néven.

#include 
#include 

#define NUM_DAYS 60

int
main(void) {
	int reserve[NUM_DAYS];
	int num_rooms, num_calls;
	int first, last, need;
	int r, d, ok;
	for (d = 0; d < NUM_DAYS; d++)
		reserve[d] = 0;
	if (2 != scanf("%d%d", &num_rooms, &num_calls))
		exit(1);
	for (r = 0; r < num_calls; r++) {
		if (3 != scanf("%d%d%d", &first, &last, &need))
			exit(1);
		if (first < 0 || last < first || NUM_DAYS <= last || need < 0)
			exit(1);
		ok = 1;
		for (d = first; d <= last; d++) {
			if (num_rooms < reserve[d] + need)
				ok = 0;
		}
		if (ok) {
			for (d = first; d <= last; d++)
				reserve[d] += need;
			printf("1\n");
		} else {
			printf("0\n");
		}
	}
	return 0;
}

A num_rooms változó tárolja az összes kiadható szoba számát. A reserve tömb d indexű elemében azt tároljuk, hány szobát foglaltak le az d sorszámú éjszakára eddig. Sikeres foglalásnál kétszer megyünk végig az általa kért napokon: először azért, hogy ellenőrizzük, mindegyik napra van-e elég szoba, másodszor azért, hogy ezeket a szobákat ténylegesen le is foglaljuk.

Most az ön feladata a következőben tér el ettől. A szálláson egy- és kétágyas szobák is vannak. Minden telefonáló megmondja, hogy hány egyágyas, és hány kétágyas szobát szeretne. A szobák nem helyettesíthetők egymással, tehát nem szabas egyágyas helyett kétágyas, vagy kétágyas helyett két egyágyas szobát adni. Mindegyik foglalást akkor kell csak elfogadni, ha rendelkezésre áll a kért számú egyágyas szoba és a kért számú kétágyas szoba.

A bemenet formája a következőképpen változik. Az első sor három számból áll, ezek az egyágyas szobák száma, a kétágyas szobák száma, és a következő foglalások száma. A kimenet formátuma nem változik.

Íme egy egyszerű példa. A bemenet.

7 7 12
0 11 2 2
2 11 1 3
2 13 4 0
6 17 2 3
7 15 1 1
8 9 2 0
6 12 4 1
9 12 0 2
2 10 0 2
5 11 0 1
5 15 1 0
11 16 1 2

A kimenet.

1
1
1
0
0
0
0
1
0
0
0
0

az első három telefonáló együtt a 2. éjszakától a 11. éjszakáig lefoglalja mind a hét egyágyas szobát, ezért a következő négy telefonálót elutasítjuk. A nyolcadik telefonáló csak kétágyas szobát kér, és abból pont maradt kettő, ezért őt ki tudjuk szolgálni. Ezzel viszont a 2. éjszakától a 10-ig már minden szoba tele van, ezért az utolsó négy telefonálót elutasítjuk.

Több példa is van.

z1f2be0 -> z1f2ki0

z1f2be1 -> z1f2ki1

z1f2be2 -> z1f2ki2

z1f2be3 -> z1f2ki3

z1f2be4 -> z1f2ki4

z1f2be5 -> z1f2ki5

A módosított feladat megoldását a SIO-ra a z1f2 feladathoz kell feltölteni.

3. feladat (z1f3): algebrai kifejezések kirajzolása

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 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.

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 [-] jelekkel kell feltölteni.

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 [+] illetve mínusz [-] karaktert kell elhelyezni.

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 [(] 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.

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ő.

+--^/ab/12*+cd+1a/-ad^b2/^b2*ac

A hagyományos alakja egész kifejezésként kiírva pedig az alábbi.

      1
     ---
      2                                  2
( a )                         a - d     b
(---)    - (c + d) (1 + a) - ------- + -----
( b )                           2       a c
                               b

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ő.

z0f7be0 -> z0f7ki0

z0f7be1 -> z0f7ki1

z0f7be2 -> z0f7ki2

Ezt az egyszerűbb feladatot a z0f7.c program oldja meg. Íme a forráskód.

#include 
#include 
#include 

int
max(int x, int y) {
	return x < y ? y : x;
}

#define PLANBUF_LEN 512

#define PREC_ATOM 0
#define PREC_POWER 2
#define PREC_QUOTIENT 3
#define PREC_PRODUCT 4
#define PREC_SUM 5

struct
plan {
	int width;
	int height;
	int depth;
	int prec;
	int head;
	const struct plan *lhs;
	const struct plan *rhs;
} planbuf[PLANBUF_LEN];
int
plancnt;

void
init(void) {
	plancnt = 0;
}

struct plan *
allocplan(void) {
	struct plan *r;
	r = planbuf + plancnt++;
	if (PLANBUF_LEN < plancnt)
		exit(1);
	r->lhs = r->rhs = 0;
	r->prec = r->head = -1;
	r->width = r->height = r->depth = 32767;
	return r;
}

const struct plan *
parenthisize(const struct plan *l) {
	struct plan *p;
	p = allocplan();
	p->head = '(';
	p->lhs = l;
	p->width = l->width + 2;
	p->height = l->height;
	p->depth = l->depth;
	p->prec = PREC_ATOM;
	return p;
}

const struct plan *
layout(const char **formulap) {
	struct plan *p;
	char head;
	const struct plan *l, *r;
	p = allocplan();
	p->head = head = *(*formulap)++;
	if (isdigit(head) || islower(head)) {
		p->width = 1;
		p->height = 1;
		p->depth = 0;
		p->prec = PREC_ATOM;
	} else {
		l = layout(formulap);
		r = layout(formulap);
		if ('^' == head) {
			if (PREC_POWER <= l->prec)
				l = parenthisize(l);
			p->width = l->width + r->width;
			p->height = l->height + r->depth + r->height;
			p->depth = l->depth;
			p->prec = PREC_POWER;
		} else if ('/' == head) {
			p->width = 2 + max(l->width, r->width);
			p->height = 1 + l->height + l->depth;
			p->depth = r->height + r->depth;
			p->prec = PREC_QUOTIENT;
		} else if ('*' == head) {
			if (PREC_PRODUCT < l->prec)
				l = parenthisize(l);
			if (PREC_PRODUCT <= r->prec)
				r = parenthisize(r);
			p->width = 1 + l->width + r->width;
			p->height = max(l->height, r->height);
			p->depth = max(l->depth, r->depth);
			p->prec = PREC_PRODUCT;
		} else if ('+' == head || '-' == head) {
			if (PREC_SUM <= r->prec)
				r = parenthisize(r);
			p->width = 3 + l->width + r->width;
			p->height = max(l->height, r->height);
			p->depth = max(l->depth, r->depth);
			p->prec = PREC_SUM;
		} else 
			exit(1);
		p->lhs = l;
		p->rhs = r;
	}
	return p;
}

#define PAPER_WIDTH 500
#define PAPER_HEIGHT 100

char
paper[PAPER_HEIGHT][PAPER_WIDTH];
int
box_height, box_width;

void
erase(const struct plan *p) {
	int r, c;
	box_height = p->height + p->depth;
	box_width = p->width;
	if (PAPER_HEIGHT < box_height || PAPER_WIDTH < box_width)
		exit(1);
	for (r = 0; r < box_height; r++)
		for (c = 0; c < box_width; c++)
			paper[r][c] = ' ';
}

void
output(void) {
	int r, w, c;
	for (r = 0; r < box_height; r++) {
		w = box_width;
		while (' ' == paper[r][w - 1])
			w--;
		for (c = 0; c < w; c++)
			putchar(paper[r][c]);
		putchar('\n');
	}
	putchar('\n');
}

void
paint(const struct plan *p, int row, int col) {
	int r, c;
	int head;
	if (
		row - p->height + 1 < 0 || 
		box_height < row + p->depth ||
		col < 0 ||
		box_width < col + p->width
	)
		exit(1);
	head = p->head;
	switch (head) {
		case '(':
			for (r = row - p->height + 1; r <= row + p->depth; r++) {
				paper[r][col] = '(';
				paper[r][col + p->width - 1] = ')';
			}
			paint(p->lhs, row, 1 + col);
			break;
		case '^':
			paint(p->lhs, row, col);
			paint(p->rhs, row - p->lhs->height - p->rhs->depth, col + p->lhs->width);
			break;
		case '/':
			for (c = col; c < col + p->width; c++)
				paper[row][c] = '-';
			paint(p->lhs, row - 1 - p->lhs->depth, col + (p->width - p->lhs->width)/2);
			paint(p->rhs, row + p->rhs->height, col + (p->width - p->rhs->width)/2);
			break;
		case '*':
			paint(p->lhs, row, col);
			paint(p->rhs, row, 1 + col + p->lhs->width);
			break;
		case '+': case '-':
			paint(p->lhs, row, col);
			paper[row][1 + col + p->lhs->width] = head;
			paint(p->rhs, row, 3 + col + p->lhs->width);
			break;
		default:
			if (isdigit(head) || islower(head))
				paper[row][col] = head;
			else
				exit(1);
			break;
	}
}

void
paint_main(const struct plan *p) {
	paint(p, p->height - 1, 0);
}

#define FORMULA_LEN 512

int
main(void) {
	char formulabuf[FORMULA_LEN];
	const struct plan *p;
	const char *formula;
	while (fgets(formulabuf, FORMULA_LEN, stdin)) {
		formula = formulabuf;
		init();
		p = layout(&formula);
		if ('\n' != *formula)
			exit(1);
		erase(p);
		paint_main(p);
		output();
	}
	return 0;
}

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 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.

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 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 paper mátrixra rajzol. Végül a mátrix alapján kiírjuk az eredményt.

Mármost önnek azzal kell bővíteni a programot, hogy binomiális együtthatókat kezeljen. Az új szabályok a következők.

Legyenek x és y algebrai kifejezések, amiknek a prefix alakjai rendre X és Y. Ekkor az x alatt y binomiális együttható is egy algebrai kifejezés, aminek a prefix alakja ?XY. A binomiális együtthatót kisértetiesen hasonlóan rajzoljuk ki hagyományos alakban, mint az x/y hányadost – érdemes is kiindulni a hányadost előállító két kódrészletből.

A hányadoshoz hasonlóan a binomiális együttható hagyományos alakja is egy sorral magasabb, mint a két részkifejezés hagyományos alakja összesen. Az x-nek megfelelő táblázat fölülre kerül, az y-nak megfelelő alulra, így középen marad egy üres sor, ez a sor lesz az egész kifejezés alapsora. Vízszintesen a két résztáblázatot úgy igazítjuk, hogy a közepük egymás alatt legyen, vagy a keskenyebbnek egy fél karakterrel balrább legyen a közepe, mint a hosszabbé. A binomiális együttható táblázata jobbra és balra is egy oszloppal szélesebb, mint a hosszabbik kifejezés.

A binomiális együttható a következőkben tér el a hányadostól. Törtvonalat nem kell rajzolni. A jobb és bal oldali extra oszlopot, amibe a két alkifejezés táblázata nem lóg bele, teljes magasságig ki kell tölteni bal [(] illetve jobb [)] zárójel karakterekkel. Végül a binomiális együtthatót sosem szabad újra zárójelbe rakni, még akkor sem, ha egy hatvány alapjában áll; továbbá a binomiális együttható alsó és felső részkifejezését sem kell sosem zárójelezni.

Így például a (6 alatt 3)2 + (8 alatt 4/2) kifejezés prefix alakja

+^?632?8/42

hagyományos alakja pedig kiírva

   2
(6)    ( 8 )
( )  + (   )
(3)    ( 4 )
       (---)
       ( 2 )

A bemenet és kimenet formája, illetve a program többi szabályai nem változnak. Íme két nagyobb példa.

z1f3be0 -> z1f3ki0

z1f3be1 -> z1f3ki1

Az ön feladata megváltoztatni a programot a fent leírt módosítások szerint, hogy a binomiális együtthatókat is támogassa. A módosított feladatot a SIO-ba z1f3 néven kell beküldeni.