PilsProg 2008
Výsledky kvalifikace
| Splněných úloh | Počet soutěžících |
|---|---|
| 5 | 3 |
| 4 | 1 |
| 3 | 3 |
| 2 | 4 |
| 1 | 2 |
Pozn.: Jména a příjmení soutěžících byla odstraněna, protože pominul účel zpracování osobních údajů.
Výsledky finále
| Splněných úloh | Počet finalistů |
|---|---|
| 4 | 3 |
| 2 | 3 |
| 1 | 2 |
| 0 | 1 |
Pozn.: Jména a příjmení finalistů byla odstraněna, protože pominul účel zpracování osobních údajů.
A jaké bylo finále?
Harmonogram PilsProg 2008
- Informace o existenci soutěže a jejích pravidlech zveřejněna: 17.1.2008
- Termín zahájení registrace: 18.1.2008
-
Vyhlášení soutěže s termíny zahájení a ukončení kvalifikace:
28.1.2008 12:00 - 10.3.2008 14:00 - Vyhlášení výsledků kvalifikačního kola: 10.3.2008
- Zveřejnění zadání úloh a jejich vybraných řešení z kvalifikačního kola: 18.3.2008
- Zveřejnění termínu a místa konání finále: 5.4.2008, 9:00-13:00, KIV - UU409.
- Vyhlášení výsledků finále: 5.4.2008
- Zveřejnění zadání a vybraných řešení úloh finálového kola: 5.4.2008
Ilustrační úlohy:
| Název | Číslo | Úspěšných řešitelů |
|---|---|---|
| AxB | 0704 | 16 |
| Pascalův trojúhelník | 0703 | 16 |
| Největší ze tří celých čísel | 0806 | 6 |
| AplusB | 0805 | 6 |
Kvalifikační úlohy:
| Název | Číslo | Úspěšných řešitelů |
|---|---|---|
| Tělocvikářův problém | 0825 | 6 |
| Petr se stěhuje | 0824 | 6 |
| Loďka | 0823 | 2 |
| Koza | 0822 | 12 |
| Kam vedou všechny cesty | 0821 | 5 |
| Bakterie | 0820 | 12 |
Finálové úlohy:
| Název | Číslo | Úspěšných řešitelů |
|---|---|---|
| Cizinec | 0850 | 3 |
| Oblázky | 0851 | 5 |
| Převozník | 0852 | 4 |
| Sekvence čísel | 0853 | 8 |
Kvalifikační úlohy - řešení:
Příklady zdrojových kódů jsou vybrány z odevzdaných soutěžních úloh.
Bakterie
Počet žijících bakterií je dán Fibonacciho posloupností rozepsanou po 1/2 hodinách (v našem případě započítáváme i umírající bakterii - viz ilustrační příklad), stačí realizovat pomocí dvou proměnných (pole) a spočíst pro příslušný počet hodin a výsledek násobit počtem bakterií na počátku.
0 1
1 2 = 3 po 1 hod
3 5 = 8 po 2 hod
8 13 = 21 po 3 hod
21 34 = 55 po 4 hod násobeno počátečním počtem bakterií
...
Příklad zdrojového kódu:
#include <stdio.h>
int Bakterie(int zije, int cas) {
if(cas > 0) {
if(zije)
return (Bakterie(1, cas - 1) + Bakterie(0, cas - 1));
else return Bakterie(1, cas - 1);
} else return 1;
}
int main(void){
int cas, pocet; scanf("%d %d", &pocet, &cas);
printf("%d", pocet * Bakterie(1, cas * 2));
return 0;
}
Kam vedou všechny cesty
Nejkratší cestu nalezneme tak, že zjistíme cestu z obou měst do Říma a najdeme společná města, kterými tyto cesty procházejí. Když potom máme jít z města A do města B, vydáme se z města A do Říma, ale v prvním městě, které má cesta z města A společné s cestou z města B do Říma, odbočíme a půjdeme do města B. Samozřejmě, nemají-li cesty z obou měst do Říma žádné společné město, pak nastane nejhorší možný případ a budeme muset projít Římem.
Příklad zdrojového kódu:
#include <stdio.h>
#include <math.h>
int main(void) {
int z, h, r[26], i, j, log[26], li;
const int R = 'R' - 65;
scanf("%d %d\n", &z, &h);
for (i = 0; i < z; i++) {
int potomek, rodic;
rodic = getchar() - 65;
while (getchar()!=' ');
potomek = getchar() - 65;
while (getchar()!='\n');
r[potomek] = rodic;
}
for (i = 0; i < h; i++) {
int odkud, kam, dOdkud, dKam;
odkud = getchar() - 65;
while (getchar()!=' ');
kam = getchar() - 65;
while (getchar()!='\n');
dOdkud = odkud;
while (1) {
putchar(dOdkud + 65);
dKam = kam;
li = 1;
log[0] = kam;
while (dKam != dOdkud && dKam != R)
log[li++] = dKam = r[dKam];
if (dKam == dOdkud) {
for (j = li-2; j >= 0; j--)
putchar(log[j] + 65);
break;
}
else {
dOdkud = r[dOdkud];
}
}
putchar('\n');
}
getchar();
return 0;
}
Koza
Způsob řešení je patrný z následujícího obrázku:
Příklad zdrojového kódu:
import java.util.Scanner;
public class Koza {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int r1 = sc.nextInt();
int r2 = sc.nextInt();
double pom = (double)r2/(2*(double)r1);
double b = 2*Math.acos(pom);
double a = 2*(Math.PI-b);
double sOhrady = Math.PI*r1*r1;
double sp1 = (((r1*r1)/2)*(a-Math.sin(a)));
double sp2 = (((r2*r2)/2)*(b-Math.sin(b)));
double sSpaseno = sp1+sp2;
if(sOhrady/2<=sSpaseno){
System.out.println("Koza je napasena");
} else {
System.out.println("Koza ma hlad");
}
}
}
Loďka
Pro skupinu do tří lidí (včetně) je strategie převezení jasná. Pro čtyřčlennou skupinu máme na výběr ze dvou možností. Lidi označíme a-d vzestupně podle časů a konečné časy označíme t1, respektive t2.
Strategie 1:
a d
a
a c
a
a b
t1 = 2a + b + c + d
Strategie 2:
a b
a
c d
b
a b
t2 = a + 3b + d
Kterou z těchto dvou strategií vybrat, rozhodneme podle konkrétních časů t.
t1 = t2
2a + b + c + d = a + 3b + d
a + c = 2b
c = 2b - a
Pokud tedy bude čas třetího člověka větší než rozdíl dvojnásobku času druhého a času prvního, je výhodnější použít strategii 2. V opačném případě je výhodnější první strategie.
Na začátku vložíme všechny časy do pole, které je nutné následně seřadit. Označíme-li jednotlivé časy podle jejich indexů v poli (0,1,2...n-1), pak budeme tvořit vždy tyto čtveřice: 0, 1, n-2, n-1. Zjistíme, která strategie je pro každou takovou skupinu výhodnější a tu použijeme (v obou strategiích ovšem vynecháváme poslední krok, takže první dva lidé s nejmenšími časy (tj. nejrychlejší) zůstanou na výchozí straně řeky). Po převezení dvou nejpomalejších lidí aplikujeme postup na další dva poslední v poli, dokud nám nezbudou 2 nebo 3 nejrychlejší na počáteční straně řeky. Ty už jednoduše převezeme.
Příklad zdrojového kódu:
program lodka;
var a:array [1..100] of word;
n,i,j,v,x:word;
begin
v:=0;
readln(n);
for i:=1 to n do
readln(a[i]);
for i:=1 to (n-1) do
begin
for j:=1 to (n-i) do
if a[j]>a[j+1] then
begin x:=a[j+1];
a[j+1]:=a[j];
a[j]:=x;
end;
end;
while n>3 do
begin
if (a[2]<= (a[n-1]+a[1]) div 2) then
v:=v+a[n]+2*a[2]+a[1]
else
v:=v+a[n]+a[n-1]+2*a[1];
n:=n-2;
end;
if odd(n)=true then
v:=v+a[2]+a[1]+a[3]
else v:=v+a[2];
writeln(v);
readln;
end.
Peter se stěhuje
Načteme počet domů, vytvoříme příslušně velké pole a uložíme do něj čísla domů, která následují na vstupu. Toto pole seřadíme a určíme z něho medián (tj. pocet_prvku_pole/2), u lichého čísla se zaokrouhlí nahoru a dostaneme medián, u sudého dostaneme „nižší medián“, ale v případě sudého počtu domů budou vzdálenosti obou „mediánů“ stejné). Následně jen spočítáme součet rozdílů prvků nalevo a napravo od mediánu a vypíšeme.
Příklad zdrojového kódu:
import java.util.*;
public class Stehovani {
public static void main(String [] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int [] p=new int [n];
for(int I=0;I<n;I++)
p[I]=sc.nextInt();
int nejmensi=p[0];
int nejvetsi=p[0];
for(int I=1;I<n;I++) {
if(p[I]<nejmensi)
nejmensi=p[I];
if(p[I]>nejvetsi)
nejvetsi=p[I];
}
int nejkratsi=0;
int soucet=0;
int vzd=0;
for(int I=0;I<n;I++)
nejkratsi+=p[I];
for(int I=1;I<=nejvetsi;I++) {
for(int i=0;i<n;i++) {
vzd=I-p[i];
if(vzd<0)
vzd=-vzd;
soucet+=vzd;
}
if(soucet<nejkratsi)
nejkratsi=soucet;
soucet=0;
} System.out.print(nejkratsi);
}
}
Tělocvikářův problém
Nejdříve najdeme žáka, který má být úplně na konci řady (nejmenší) a pak zjistíme jestli žák, který má být hned před ním, je před ním (klidně mezi nimi může být několik žáků), potom zjišťujeme jestli třetí žák od konce je před druhým a tak pořád, dokud nezjistíme že x-tý žák od konce (na pořadníku) není před (x-1)-tým žákem od konce (na pořadníku). Žáci, kteří jsou na pořadníku na x-tém a vyšším místě postupně vystoupí tak, že první vystoupí x-tý a poslední ten, co má být úplně první v řadě.
Například:
PŘED PO 1.VÝSTUP 2. VÝSTUP
John Paul George Paul
Paul George John George
George John Paul John
Ringo Ringo Ringo Ringo
Příklad zdrojového kódu:
program Telocvikar;
var a,b:array [1..200] of string;
k,l,m,n,i:word;
c,d,e:string;
j:boolean;
begin readln(n);
m:=n;
for i:=1 to n do
readln(a[i]);
for i:=1 to n do
readln(b[i]);
while m>1 do
begin
c:=b[m];
d:=b[m-1];
for i:=1 to n do
begin
if (a[i]=c) or (a[i]=d)
then
if a[i]=c
then j:=true
else j:=false;
if (a[i]=c) or (a[i]=d)
then break
else continue;
end;
if j=true then
begin
for k:=i to n do
begin
if a[k]=d then
begin e:=a[k];
writeln(e);
for l:=k-1 downto 1 do
a[l+1]:=a[l];
a[1]:=e;
end;
end;
end;
m:=m-1;
end;
readln;
end.
Finálové úlohy - řešení:
Příklady zdrojových kódů jsou vybrány z odevzdaných soutěžních úloh či řešení testerů úloh.
Cizinec
Vytvoříme typické slovníkové uspořádání (dvojrozměrné pole pro n dvojjazyčných výrazů). provedeme řazení podle cizojazyčných výrazů (pro velký objem dat – Quick sort). Čtená cizojazyčná slova vyhledáváme ve slovníku sekvenčně (pro velký objem dat - binární vyhledávání). V případě, že slovo nalezneme, provedeme výpis jeho anglického ekvivalentu, tj. slova na stejném indexu v poli. v opačném případě vypíšeme řetězec „eh“.
Příklad zdrojového kódu:
import java.util.Scanner;
public class Cizinec {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String[] radka;
String[][] slovnik = new String[5000][2];
int citac = 0;
while (true) {
radka = sc.nextLine().split(" ");
if (radka.length == 1) {
break;
}
slovnik[citac][0] = radka[0];
slovnik[citac][1] = radka[1];
citac++;
}
while (sc.hasNextLine()) {
radka[0] = sc.nextLine();
if (radka[0].length() == 0) {
break;
}
boolean nalezeno = false;
for (int i = 0; i < citac; i++) {
if (slovnik[i][1].equals(radka[0])) {
System.out.println(slovnik[i][0]);
nalezeno = true;
break;
}
}
if (!nalezeno) {
System.out.println("eh");
}
}
}
}
Oblázky
Pro strategii hry při variantě - "prohrává ten, na koho zbude poslední odebrání oblázku" - platí:
x = (p - 1) mod (n +1)
kde:
p ... počet oblázků před příslušným tahem ve hře
n ... největší počet oblázků, který je dovolen odebrat jedním tahem
x ... počet odebraných oblázků v příslušném tahu, aby zůstala zachována možnost výhry
Pro první tah stačí otestovat, zda x = 0, neboť v tom případě je hra pro začínajícího prohraná (pokud protihráč už neudělá v další hře nějaký chybný tah) a tudíž necháme táhnout Lojzu, v opačném případě x udává počet oblázků, které je nutno odebrat.
Příklad zdrojového kódu:
import java.util.*;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int p = sc.nextInt();
int n = sc.nextInt();
int x = (p-1) % (n+1);
if (x!=0)
System.out.println (x);
else
System.out.println ("Lojza");
}
}
Převozníkův úkol
Břehům přiřadíme logickou hodnotu. Všichni účastníci mají na začátku stejnou hodnotu břehu. Vstupní kombinaci (uloženou v řetězci) ověřujeme postupně tak, že při jednotlivých převozech (změna hodnoty) kontrolujeme stav na břehu (březích), kde není převozník (jen tam jsou totiž možné kolize, tj. sežrání).
Příklad zdrojového kódu:
import java.util.Scanner;
public class Prevoznik {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String radek = sc.nextLine();
boolean p = true;
boolean z = true;
boolean k = true;
boolean v = true;
for (int i = 0; i < radek.length(); i++) {
if (radek.charAt(i) == 'p') {
p = !p;
}
if (radek.charAt(i) == 'p' && (i + 1 < radek.length()) && radek.charAt(i + 1) != 'p') {
i++;
if (radek.charAt(i) == 'z') {
z = !z;
}
else
if (radek.charAt(i) == 'k') {
k = !k;
}
else
if (radek.charAt(i) == 'v') {
v = !v;
}
}
if (p) {
if ((!z && !k) || (!k && !v)) {
System.out.println("dojde k sezrani");
return;
}
}
else {
if ((z && k) || (k && v)) {
System.out.println("dojde k sezrani");
return;
}
}
}
System.out.println("spravny postup");
}
}
Sekvence čísel
Výpočtem v zadání popsaným způsobem:
- liché: 3n+1
- sudé: /2
Délky cyklu všech čísel mezi - reprezentace dvěma cykly do sebe vnořenými s uchováním maxima.
Např.:
1 4
1 4 8
4 2 1 = 3
3 10 5 16 8 4 2 1 = 8 - tj. maximum!
2 1 = 2
Příklad zdrojového kódu:
import java.util.Scanner;
public class SekvenceCisel {
public static int delkaCyklu(int cislo) {
int delka = 1;
while (true) {
if (cislo == 1) {
return delka;
}
else {
if (cislo % 2 == 0) {
cislo /= 2;
}
else {
cislo *= 3;
cislo++;
}
delka++;
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
int j = sc.nextInt();
boolean prohozeno = false;
if (i > j) {
int pomocna = i;
i = j;
j = pomocna;
prohozeno = true;
}
int maxHodnota = delkaCyklu(i);
int hodnota;
for (int citac = i + 1; citac <= j; citac++) {
hodnota = delkaCyklu(citac);
if (hodnota > maxHodnota) {
maxHodnota = hodnota;
}
}
if (!prohozeno) {
System.out.println("" + i + " " + j + " " + maxHodnota);
}
else {
System.out.println("" + j + " " + i + " " + maxHodnota);
}
}
}


