Задаци: Позициони запис

Алгоритми и програми у програмском језику C: Позициони запис.

Збир цифара четвороцифреног броја

Прочитај текст задатка.

Збир цифара четвороцифреног броја се добија издвајањем цифара јединица, десетица, стотина и хиљада, које се затим саберу. Цифре броја можемо одредити на неколико различитих начина.

Кључна опаска је увек то да се последња цифра броја може одредити операцијом израчунавања остатка при дељењу са 10. На пример, последња цифра броја 1234 је 4, што је управо остатак при дељењу тог броја са 10.

До цифре јединица \(c_0\) броја \(n\) може се лако доћи као \(n\mod{10}\). На пример, цифра јединица броја 1234 је 4 и она се лако добија као остатак при дељењу броја 1234 са 10.

Последња цифра броја се лако може уклонити тако што се израчуна целобројни количник тог броја са 10. На пример, целобројни количник при дељењу броја 1234 са 10 је 123. Цифру десетица полазног броја можемо добити као цифру јединица броја који се добија када му се избрише последња цифра, а управо смо видели да је то број \(n\div{10}\). На пример, цифра десетица број 1234 је цифра јединица броја 123 и она је једнака \((n\div{10})\mod{10}\).

Последње две цифре броја се могу уклонити тако што се израчуна количник тог броја при дељењу са 100. На пример, целобројни количник при дељењу броја 1234 са 100 је 12. Цифру стотина полазног броја можемо добити као цифру јединица броја који се добија када му се избришу последње две цифре, а управо смо видели да је то број \(n\div{100}\). На пример, цифра стотина броја 1234 је цифра јединица броја 12 и она је једнака \((n\div{100})\mod{10}\).

Последње три цифре броја се могу уклонити тако што се израчуна количник тог броја при дељењу са 1000. На пример, целобројни количник при дељењу броја 1234 са 1000 је 1. Цифру хиљада полазног броја можемо добити као цифру јединица броја који се добија када му се избришу последње три цифре, а управо смо видели да је то број \(n\div{1000}\). На пример, цифра хиљада броја 1234 је цифра јединица броја 1 и она је једнака \((n\div{1000})\mod{10}\).

Уочава се веома јасна правилност.

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int broj;
    scanf("%d", &broj);
    int cifraJedinica = (broj / 1) % 10;
    int cifraDesetica = (broj / 10) % 10;
    int cifraStotina = (broj / 100) % 10;
    int cifraHiljada = (broj / 1000) % 10;
    int zbirCifara = cifraJedinica + cifraDesetica + cifraStotina + cifraHiljada;
    printf("%d", zbirCifara);
    return 0;
}

Јарди, стопе и инчи

Прочитај текст задатка.

Класичан поступак конверзије подразумева да се израчуна колико у једном јарду има инча (\(3\cdot 12=36\)) и да се тај број помножи бројем јарди, да се број инча у једној стопи (12) помножи бројем стопа и на крају да се на збир ова два броја дода број инча. Тиме добијамо формулу \(j\cdot 36+s\cdot 12+i\).

Предложено решење задатка (1)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int jardi, stope, inci;
    scanf("%d%d%d", &jardi, &stope, &inci);
    int uIncima = jardi * 36 + stope * 12 + inci;
    printf("%d", uIncima);
    return 0;
}

У основи претходне формуле лежи један поступак који нам омогуаћава да поступно извршимо конверзију. Прво можемо да се запитамо колико стопа има у учитаној дужини. Пошто у једној јарди има 3 стопе у \(j\) јарди имамо \(j\cdot 3\) стопa и поред тога имамо још \(s\) стопа. Укупан број стопа је зато једнак \(j\cdot 3+s\).

Сада се можемо запитати колико укупно имамо инча. У свакој од стопа које смо мало пре израчунали има 12 инча и имамо још преосталих \(i\) инча. Зато је укупан број инча једнак \((j\cdot 3+s)\cdot 12+i\).

Овакав запис се назива Хорнерова схема и она се често користи за вредности бројева и полинома. На пример, вредност броја \(123=1\cdot 100+2\cdot 10+3\), се Хорнеровом схемом може израчунати као \(((1\cdot 10)+2)\cdot 10+3\), па и као \(((0\cdot 10+1)\cdot 10+2)\cdot 10+3\).

Предложено решење задатка (1)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int jardi, stope, inci;
    scanf("%d%d%d", &jardi, &stope, &inci);
    int uIncima = ((jardi * 3) + stope) * 12 + inci;
    printf("%d", uIncima);
    return 0;
}

Октални бројеви

Прочитај текст задатка.

За вредности броја на основу цифара се може употребити алгоритам заснован на класичној дефиницији позиционог записа (тада број израчунавамо као \(c_3\cdot 8^3+c_2\cdot 8^2+c_1\cdot 8^1+c_0\cdot 8^0\)), док цифру на позицији \(i\) можемо израчунати као \((n\div 8^i)\mod 8\).

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int c3, c2, c1, c0, n;
    scanf("%d%d%d%d%d", &c3, &c2, &c1, &c0, &n);
    printf("%d\n", c3 * 8 * 8 * 8 + c2 * 8 * 8 + c1 * 8 + c0);
    c0 = (n / (1)) % 8;
    c1 = (n / (8)) % 8;
    c2 = (n / (8 * 8)) % 8;
    c3 = (n / (8 * 8 * 8)) % 8;
    printf("%d%d%d%d", c3, c2, c1, c0);
    return 0;
}

Избаци цифру стотина

Прочитај текст задатка.

Пре избацивања цифре, одредимо префикс броја пре те цифре (део записа броја испред ње) и суфикс броја након те цифре (део записа броја иза ње). На пример, ако је број 123456, префикс пре цифре стотина је 123, а суфикс након ње је 56. Префикс се може одредити као целобројни количник овог броја са 1000, а суфикс као остатак при дељењу са 100. На крају ћемо на префикс дописати цифре суфикса. Пошто је суфикс двоцифрен, то ћемо урадити тако што ћемо префикс помножити са 100 и сабрати са суфиксом.

Доказ коректности. Докажимо ово и формално. Претпоставимо да је \(n\) облика \((c_kc_{k-1}\ldots c_3c_2c_1c_0)_{10}\), тј.

\[n=c_k\cdot 10^k+\ldots c_3\cdot 10^3+c_2\cdot 10^2+c_1\cdot 10+c_0\]

Овај број се може записати у облику \((c_k\cdot 10^{k-3}+\ldots +c_3)\cdot 10^3+c_2\cdot 10^2+c_1\cdot 10+c_0\) и пошто важи да је \(0\leq c_2\cdot 10^2+c_1\cdot 10+c_0<1000\) (јер су све цифре између 0 и 9), на основу дефиниције целобројног количника, важи да је \(n\div 1000=c_k\cdot 10^{k-3}+\ldots +c_3\). Потпуно аналогно се доказује да је \(n\mod 100=c_1\cdot 10+c_0\). На крају, важи да је

\[100\cdot(n\div 1000)+n\mod 100=c_k\cdot 10^{k-1}+\ldots c_3\cdot 10^2+c_1\cdot 10+c_0\]

што је број \((c_kc_{k-1}\ldots c_3c_1c_0)_{10}\) који се добија од полазног брисањем цифре \(c_2\).

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int n;
    scanf("%d", &n);
    int prefiks = n / 1000;
    int sufiks = n % 100;
    printf("%d", 100 * prefiks + sufiks);
    return 0;
}

Поноћ

Прочитај текст задатка.

Један веома често коришћен начин је да се пре израчунавања све преведе у најмање јединице, а да се након израчунавања изврши прерачунавање у полазне јединице. То би подразумевало да се текући тренутак и наредна поноћ претворе у број секунди протекао од претходне поноћи, да се израчуна број секунди до поноћи и да се затим то време изражено само у секундама изрази у сатима, минутима и секундама.

Ако је време одређено бројевима \(h\), \(m\) и \(s\) који представљају број сати, минута и секунди, тада се време протекло од почетка дана може израчунати тако што се саберу број сати помножен са \(60\cdot 60\) (јер у једном сату има 60 минута, а сваки од њих има 60 секунди), број минута помножен са \(60\) (jер у једном минуту има 60 секунди) и број секунди, тј. израчунавањем вредности \(h\cdot 60^2+m\cdot 60+s\), што може да се уради било директно, било Хорнеровим поступком. Приметимо да се овде ради о запису у бројевној основи \(60\). Хорнеров поступак би овде кретао прво од броја целих сати \(h\), затим израчунавао број целих минута тако што би помножио број сати \(h\) са \(60\) и на то додао број \(m\), а затим број секунди добио множећи тај број минута са \(60\) и сабирајући са \(s\) (тј. израчунао би вредност израза \((h\cdot 60+m)\cdot 60+s\)). Наредна поноћ се може представити у облику времена 24:00:00 и она се може превести у секунде на потпуно исти начин (изразом \(24\cdot 60^2\)).

Када се изврши одузимање, тада је добијени број секунди (рецимо да је то \(t_s\)) потребно претворити у сате, минуте и секунде. Последња цифра (вредност \(s\)) може се одредити као остатак при дељењу броја \(t_s\) са \(60\) тј. \(s=t_s\mod 60\), претпоследња цифра \(m\) као остатак при дељењу са \(60\) целобројног количника бројева \(t_s\) и 60 тј. \(m=(t_s\div 60)\mod 60\), а прва цифра \(h\) као целобројни количник бројева \(t_s\) и \(60^2\) тј. \(h=t_s\div 60^2\). Ако у полазном броју секунди има више од једног дана (у овом задатку то није могуће, али у неким другим задацима ће бити), тада је потребно још одредити остатак броја сати при дељењу са \(24\). Овим се, дакле, добија следећи поступак.

\[\begin{split}\begin{eqnarray*} s &=& t_s\mod 60\\ m &=& (t_s\div 60)\mod 60\\ h &=& (t_s\div 60^2)\mod 24 \end{eqnarray*}\end{split}\]

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int h, m, s;
    scanf("%d%d%d", &h, &m, &s);
    int S = h * 60 * 60 + m * 60 + s;
    int Sponoc = 24 * 60 * 60;
    int S_do_ponoci = Sponoc - S;
    int s_do_ponoci = (S_do_ponoci / (1)) % 60;
    int m_do_ponoci = (S_do_ponoci / (60)) % 60;
    int h_do_ponoci = (S_do_ponoci / (60 * 60)) % 24;
    printf("%d\n%d:%d:%d", S, h_do_ponoci, m_do_ponoci, s_do_ponoci);
    return 0;
}

Трајање вожње

Прочитај текст задатка.

Трајање вожње израчунава се тако што се од времена доласка одузме време поласка. Задатак, дакле, захтева израчунавање разлике између два временска тренутка за која знамо да су у оквиру једног дана.

Један од уобичајених начина рада са временом је да се сваки временски тренутак изрази преко броја секунди протеклих од почетка тог дана (тј. од претходне поноћи).

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int hPocetak, mPocetak, sPocetak, hKraj, mKraj, sKraj;
    scanf("%d%d%d%d%d%d", &hPocetak, &mPocetak, &sPocetak, &hKraj, &mKraj, &sKraj);
    int SPocetak = hPocetak * 60 * 60 + mPocetak * 60 + sPocetak;
    int SKraj = hKraj * 60 * 60 + mKraj * 60 + sKraj;
    int S = SKraj - SPocetak;
    int sTrajanje = S % 60;
    int mTrajanje = (S / 60) % 60;
    int hTrajanje = (S / (60 * 60));
    printf("%d:%d:%d", hTrajanje, mTrajanje, sTrajanje);
    return 0;
}

UNIX време

Прочитај текст задатка.

Као и обично, написаћемо једначине за конверзију времена датог у данима, сатима, минутима, секундама и милисекундама у број милисекунди од укључивања рачунара и броја милисекунди у време, све свести на милисекунде, извршити тражену операцију сабирања времена и на крају, милисекунде превести у време у данима, сатима, минутима, секундама и милисекундама.

Најприроднији начин да време преведемо у милисекунде је да употребимо Хорнерову шему. Укупан број сати можемо добити тако што број дана помножимо са 24 (јер је толико сати у једном дану) и додамо број сати. Укупан број минута можемо добити тако што тако добијени број сати помножимо са 60 (јер је толико минута у једном сату) и додамо број минута. Укупан број секунди можемо добити тако што тако добијени број минута помножимо са 60 (јер је толико секунди у једном минуту) и додамо број секунди. На крају, укупан број милисекунди можемо добити тако што добијени број секунди помножимо са 1000 (јер је толико милисекунди у једној секунди) и додамо број милисекунди. Дакле, ако променљиве \(dan\), \(sat\), \(min\), \(sek\) и \(mili\) представљају број дана, сати, минута, секунди и милисекунди, тада конверзију можемо извршити помоћу израза

\[(((dan\cdot 24+sat)\cdot 60+min)\cdot 60+sek)\cdot 1000+mili\]

Ово је много лепше решење него да, на пример, унапред рачунамо колико милисекунди има у једном сату.

Конверзија у супротном смеру тече тако што се рачуна једна по једна цифра у запису у овој мешовитој основи.

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int p_dan, p_sat, p_min, p_sek, p_mili, trajanje;
    scanf("%d%d%d%d%d%d", &p_dan, &p_sat, &p_min, &p_sek, &p_mili, &trajanje);
    int pocetak = (((p_dan * 24 + p_sat) * 60 + p_min) * 60 + p_sek) * 1000 + p_mili;
    int kraj = pocetak + trajanje;
    int k_mili = (kraj / 1) % 1000;
    int k_sek = (kraj / 1000) % 60;
    int k_min = (kraj / (1000 * 60)) % 60;
    int k_sat = (kraj / (1000 * 60 * 60)) % 24;
    int k_dan = (kraj / (1000 * 60 * 60 * 24));
    printf("%d:%d:%d:%d:%d", k_dan, k_sat, k_min, k_sek, k_mili);
    return 0;
}

Угао сатне казаљке

Прочитај текст задатка.

За разлику од секундне казаљке која се креће дискретно (поскочи сваке секунде), сатна казаљка се обично креће непрекидно. Ипак, пошто у овом задатку не разматрамо секунде, већ само минуте, претпоставићемо да се сатна казаљка помера само једном у минуту (на сваком пуном минуту). Дакле, положај сатне казаљке ће бити одређен временом протеклим од претходног тренутка када је сатна казаљка била усмерена на горе (а то је претходна поноћ или претходно подне) израженим у целом броју минута.

Ако се зна колико је тренутно сати и минута, тај број се може лако израчунати (број сати се помножи са 60 и сабере се са бројем минута). Пошто угао казаљке линеарно зависи од броја протеклих минута, и пошто на сваких 12 сати сатна казаљка направи пун круг, тј. опише угао од 360 степени, важи пропорција

\[\alpha^\circ:M=360^\circ:12 \ h\]

где је \(M\) број минута протеклих од претходног тренутка када је сатна казаљка заклапала \(0^\circ\) (а то је претходно подне или поноћ). \(M\) се, дакле, може израчунати тако што се тих 12h (тј. 12 сати и 0 минута) претвори у број минута протеклих од претходне поноћи или поднева и реши се пропорција

\[M=\frac{\alpha^\circ \cdot(12\cdot 60 \ m)}{360^\circ}=\frac{\alpha^\circ \cdot 720 \ m}{360^\circ} = \alpha \cdot 2 \ m\]

Када се зна број минута протеклих од претходног поднева или поноћи, време се лако може израчунати (остатак при дељењу са 60 даје број минута, а количник број сати).

Један начин да се дође до решења је да се закључи да, пошто за 12 сати сатна казаљка обиђе угао од 360 степени, она за један сат обиђе дванаестину тог угла тј. угао од \(\frac{360^\circ}{12}=30^\circ\). Пошто један сат има 60 минута, за један временски минут она обиђе шездесетину угла који пређе за сат времена, тј. угао од \(\frac{30^\circ}{60}=0.5^\circ\). Дакле, пошто за један временски минут казаљка пређе пола степена, једном степену одговара 2 минута времена. Број минута протеклих од поднева или поноћи се онда може израчунати множењем датог угла у степенима са 2 (минута по степену).

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int alfa, m, h;
    scanf("%d", &alfa);
    int M = alfa * 2;
    h = M / 60;
    m = M % 60;
    printf("%d:%d", h, m);
    return 0;
}

Угао између казаљки

Прочитај текст задатка.

Нека је дати временски тренутак описан параметрима \(sat\) и \(minut\).

Угао који минутна казаљка заклапа у односу на почетни положај од нула минута (такозвани угаони отклон минутне казаљке) једнак је \(minut\cdot 360'\), где \('\) означава угаони минут. Заиста, на сваки минут времена минутна казаљка се помера за \(\frac{360^\circ}{60}=6^\circ\). Дакле, \(6^\circ\) је померање минутне казаљке у степенима по сваком временском минуту, а пошто у једном степену има \(60\) угаоних минута тј. пошто је \(1^\circ=60'\), за сваки минут времена минyтна казаљка се помери за \(6^\circ \cdot \frac{60'}{1^\circ}=360'\), тј. за \(360\) угаоних минута.

Подсетимо се, угао у угаоним минутима који сатна казаљка заузима у односу на положај \(12 \ h\) (угаони отклон сатне казаљке) једнак је \(sat\cdot 30^\circ\cdot \frac{60'}{1^\circ}+minut\cdot 30'\). Заиста на сваки сат казаљка се помери за \(\frac{360^\circ}{12}=30^\circ\), а пошто у једном степену има \(60'\), то је једнако \(30^\circ\cdot \frac{60'}{1^\circ}\). На сваки минут времена сатна казаљка се помери додатно за \(\frac{30^\circ}{60}=30'\). Заиста, она се за један минут времена помери \(12\) пута мање него минутна казаљка, за коју смо установили да се за минут времена помери за \(360'\). Дакле сатна казаљка се за сваки минут времена помери за \(30'\).

Да би се израчунао (неоријентисани) угао између казаљки изражен у минутима потребно је одредити апсолутну вредност разлике у угаоним минутима. На крају је добијени резултат потребно превести у степене и минуте.

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int sat, min;
    scanf("%d%d", &sat, &min);
    sat %= 12;
    int u_sat = sat * 30 * 60 + min * 30;
    int u_min = min * 360;
    int u_izmedju = abs(u_sat - u_min);
    int u_izmedju_stepeni = u_izmedju / 60;
    int u_izmedju_minuti = u_izmedju % 60;
    printf("%d:%d", u_izmedju_stepeni, u_izmedju_minuti);
    return 0;
}

Размени цифре

Прочитај текст задатка.

Цифру јединица и цифру стотина у природном броју можемо разменити тако што прво уклонимо цифре јединица и стотина, тачније заменимо их нулама, а затим цифру јединица додамо у броју на место цифре стотина, а цифру стотина додамо на место цифре јединица.

Прво одредимо цифру јединица \(c_0\) и цифру стотина \(c_2\) броја \(n\). Цифру јединица добијамо као остатaк при дељењу полазног броја са 10 (\(c_0=n\mod 10\)), а цифру стотина као остатак при дељењу са 10 количника полазног броја и броја 100 (\(c_2=(n\div 100)\mod 10\)). Затим од броја одузмемо цифру јединица \(c_0\), и одузмемо цифру стотина \(c_2\) помножену са 100 (израчунамо \(n-c_0-c_2\cdot 100\)). На тај начин смо у броју „избрисали” цифру јединица и цифру стотина (заменили их нулама). Преостаје нам да у броју додамо цифру јединица на место цифре стотина, то постижемо увећавањем броја за производ цифре јединица и броја 100, и да цифру стотина додамо на место цифре јединица, једноставно увећањем броја за цифру стотина (увећамо претходни резултат за \(c_0\cdot 100+c_2\)).

Предложено решење задатка (1)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int broj;
    scanf("%d", &broj);
    int c0 = (broj / 1) % 10;
    int c2 = (broj / 100) % 10;
    int broj_r = broj - c0 - c2 * 100 + c2 + c0 * 100;
    printf("%d", broj_r);
    return 0;
}

Замена цифре \(c_i\) која је на позицији \(i\) у броју \(n\) цифром \(c\) реализујемо изразом \(n=n-10^i\cdot c_i+10^i\cdot c\). Одузимањем од полазног броја производа \(10^i\cdot c_i\) цифру на позицији \(i\) постављамо на 0, а увећавањем добијене вредности за \(10^i\cdot c\) цифру на позицији \(i\) постављамо на вредност \(c\). Израз записујемо краће \(n+10^i\cdot(c-c_i)\). У нашем задатку то изгледа овако \(n+1\cdot(c_2-c_0)+100\cdot(c_0-c_2)\), или краће \(n+99\cdot(c_0-c_2)\).

Предложено решење задатка (2)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int broj;
    scanf("%d", &broj);
    int c0 = (broj / 1) % 10;
    int c2 = (broj / 100) % 10;
    int broj_r = broj + 99 * (c0 - c2);
    printf("%d", broj_r);
    return 0;
}

Бројање оваца

Прочитај текст задатка.

Сваки прст на Јовановој руци представља једну овцу. Сваки прст на Дулетовој руци представља 5 оваца, зато што Дуле отвара следећи прст само кад Јован изброји 5 оваца. Сваки прст на Станојевој руци представља 25 оваца, зато што Станоје отвара следећи прст само кад Дуле изброји до 5. Већ смо објаснили да сваки Дулетов прст представља 5 оваца, према томе 5 Дулетових прстију вреди \(5\cdot 5\) оваца, дакле 25. Обележимо са \(J, D, S\) број отворених прстију десне руке редом код Јована, Дулета и Станоја. Тада је број оваца у стаду једнак \(S\cdot 25+D\cdot 5+J\).

Можемо приметити да Јова и Дуле не могу имати отворена више од 4 прста, јер сваки пут кад изброје до 5, почињу бројање из почетка, а следећи пастир отвара још један прст.

Ово је пример бројања у систему са основом 5. Уопште, ако су дате цифре \(a_n,a_{n-1},\ldots,a_1,a_0\), такве да су све из интервала \([0,b-1]\), тада можемо да оформио запис \((a_na_{n-1}\ldots a_1a_0)_b\) чија је вредност једнака \(a_nb^n+a_{n-1}b^{n-1}+\ldots+a_2b^2+a_1b+a_0\).

Предложено решење задатка (1)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int j, d, s;
    scanf("%d%d%d", &j, &d, &s);
    int broj_ovaca = s * 25 + d * 5 + j;
    printf("%d", broj_ovaca);
    return 0;
}

Задатак можемо решити и Хорнеровом схемом. Тада се вредност израчунава као \((S\cdot 5+D)\cdot 5+J\).

Предложено решење задатка (2)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int j, d, s;
    scanf("%d%d%d", &j, &d, &s);
    int broj_ovaca = (s * 5 + d) * 5 + j;
    printf("%d", broj_ovaca);
    return 0;
}

Обрни цифре

Прочитај текст задатка.

Један од начина да решимо задатак је да искомбинујемо поступак уклањања цифара са десног краја броја и поступак додавања цифара на десни крај броја. Последња цифра (цифра јединица) се може одредити одређивањем остатка при дељењу бројем 10, а уклонити одређивањем целобројног количника при дељењу бројем 10. Цифра се може додати на десни крај броја тако што се број помножи са 10 и сабере са том цифром. Формирање броја додавањем једне по једне цифре на десни крај назива се Хорнеров поступак тј. Хорнерова шема.

Да бисмо решили овај конкретан задатак, четири пута ћемо уклонити цифру јединица (и те четири цифре ћемо упамтити у четири променљиве). Након тога, цифре које смо запамтили у тим променљивим ћемо једну по једну додавати на десни крај броја, али у обратном редоследу (прво ћемо додати цифру јединица, иза ње цифру десетица, затим цифру стотина и на крају цифру хиљада). На пример, ако је полазни број 1234567, запамтићемо и уклонити цифру јединица (одређивањем остатка и целобројног количника при дељењу са 10) и добићемо број 123456, затим ћемо уклонити следећу цифру и добити број 12345, затим број 1234 и на крају број 123. Након тога ћемо на тај број додати цифру јединица полазног броја (множењем са 10 и сабирањем) и добићемо 1237. Након тога ћемо на сличан начин добити 12376, након тога 123765 и на крају тражени број 1237654.

Предложено решење задатка (1)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int n;
    scanf("%d", &n);
    int c0 = n % 10; n /= 10;
    int c1 = n % 10; n /= 10;
    int c2 = n % 10; n /= 10;
    int c3 = n % 10; n /= 10;
    n = 10 * n + c0;
    n = 10 * n + c1;
    n = 10 * n + c2;
    n = 10 * n + c3;
    printf("%d", n);
    return 0;
}

Једно могуће решење је да се одреде вредности последње 4 цифре \(c_3\), \(c_2\), \(c_1\) и \(c_0\), да се одреди префикс \(p\) испред те четири цифре (он се може израчунати као целобројни количник при дељењу полазног броја са 10000) и затим да се формира нови број \(p\cdot 10000+c_0\cdot 1000+c_1\cdot 100+c_2\cdot 10+c_3\).

Предложено решење задатка (2)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int n;
    scanf("%d", &n);
    int c0 = n / 1 % 10;
    int c1 = n / 10 % 10;
    int c2 = n / 100 % 10;
    int c3 = n / 1000 % 10;
    int prefiks = n / 10000;
    printf("%d", 10000 * prefiks + c0 * 1000 + c1 * 100 + c2 * 10 + c3 * 1);
    return 0;
}

Једно могуће решење је да се одреде вредности последње 4 цифре, а затим да се на сваком од 4 места тренутна цифра замени жељеном.

Предложено решење задатка (3)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int n;
    scanf("%d", &n);
    int c0 = n / 1 % 10;
    int c1 = n / 10 % 10;
    int c2 = n / 100 % 10;
    int c3 = n / 1000 % 10;
    int r = n + 1000 * (c0 - c3) + 100 * (c1 - c2) + 10 * (c2 - c1) + 1 * (c3 - c0);
    printf("%d", r);
    return 0;
}

Цифре здесна

Прочитај текст задатка.

Једно решење је да се прво учита свих 6 цифара у 6 различитих променљивих, а онда да се вредност броја формира на стадардни начин (било као вредност полинома \(c_5\cdot 10^5+c_4\cdot 10^4+c_3\cdot 10^3+c_2\cdot 10^2+c_1\cdot 10 +c_0\) одређеног класичном дефиницијом записа декадног броја, било Хорнеровим поступком).

Предложено решење задатка (1)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int c0, c1, c2, c3, c4, c5;
    scanf("%d%d%d%d%d%d", &c0, &c1, &c2, &c3, &c4, &c5);
    int broj = 100000 * c5 + 10000 * c4 + 1000 * c3 + 100 * c2 + 10 * c1 + 1 * c0;
    printf("%d", broj);
    return 0;
}

Приказаћемо решење које неће прво учитавати све цифре, већ ће обрађивати цифру по цифру, редом којим пристижу. Број (памтићемо га у променљивој broj) иницијализујемо на нулу, и у сваком кораку учитавамо нову цифру и дописујемо је слеве стране тако што тренутну вредност броја саберемо са вредношћу цифре помножену одговарајућим тежинским фактором. У првом кораку тај фактор је 1, у наредном 10, затим 100, 1000, 10000 и на крају 100000.

Предложено решење задатка (2)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int cifra, broj = 0;
    scanf("%d", &cifra);
    broj += 1 * cifra;
    scanf("%d", &cifra);
    broj += 10 * cifra;
    scanf("%d", &cifra);
    broj += 100 * cifra;
    scanf("%d", &cifra);
    broj += 1000 * cifra;
    scanf("%d", &cifra);
    broj += 10000 * cifra;
    scanf("%d", &cifra);
    broj += 100000 * cifra;
    printf("%d", broj);
    return 0;
}

Употребићемо променљиву tezina у којој се памти тренутна вредност тежинског фактора (текући степен основе). Променљиву broj ћемо ажурирати тако што ћемо је увећати за производ тренутно учитане цифре cifra и текуће вредности променљиве tezina. Променљиву tezina иницијализујемо на вредност 1 и на крају сваког корака ажурирамо множењем са 10.

Предложено решење задатка (3)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int cifra, tezina = 1, broj = 0;
    scanf("%d", &cifra);
    broj += tezina * cifra;
    tezina *= 10;
    scanf("%d", &cifra);
    broj += tezina * cifra;
    tezina *= 10;
    scanf("%d", &cifra);
    broj += tezina * cifra;
    tezina *= 10;
    scanf("%d", &cifra);
    broj += tezina * cifra;
    tezina *= 10;
    scanf("%d", &cifra);
    broj += tezina * cifra;
    tezina *= 10;
    scanf("%d", &cifra);
    broj += tezina * cifra;
    tezina *= 10;
    printf("%d", broj);
    return 0;
}

Чекање

Прочитај текст задатка.

Растојање између два броја чији се поредак не зна, најлакше се израчунава као апсолутна вредност њихове разлике. Као што је то обично случај, аритметику над временским тренуцима је лакше спроводити ако се времена представе преко броја секунди протеклих од почетка дана. Када се време представи на тај начин, апсолутна вредност разлике се може израчунати библиотечком функцијом abs. На крају, добијени број секунди се преводи назад у време изражено сатима, минутима и секундама. Оба смера конверзије врше се на неки од уобичајених начина.

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int ah, am, as, bh, bm, bs;
    scanf("%d%d%d%d%d%d", &ah, &am, &as, &bh, &bm, &bs);
    int a = ah * 60 * 60 + am * 60 + as;
    int b = bh * 60 * 60 + bm * 60 + bs;
    int c = abs(a - b);
    int s = c % 60;
    int m = (c / 60) % 60;
    int h = c / (60 * 60);
    printf("%d:%d:%d", h, m, s);
    return 0;
}

Тркачи

Прочитај текст задатка.

Ако је

  • \(s\) - дужина кружне стазе у \(km\)

  • \(v_1\) - брзина првог тркача у \(\frac{km}{h}\)

  • \(v_2\) - брзина другог тркача у \(\frac{km}{h}\)

тада је брзина одмицања бржег тркача од споријег \(|v_1-v_2| \ kmh\). Према томе, бржи тркач пун круг - пут који треба да пређе да би сустигао споријег - прелази за време \(t=\frac{s}{|v_1-v_2|} \ h\) (користимо формулу која повезује брзину, време и пређени пут код равномерног кретања).

Апсолутну вредност можемо израчунати коришћењем библиотечке функције fabs. Новина у овом задатку је то што време не желимо да прикажемо у реалном броју сати, већ, у облику сати, минута и секунди. Претпоставимо да је резултат облика \(h\) сати, \(m\) минута и \(s\) секунди. Пошто у једном сату има \(60\) минута, а у једном минуту има \(60\) секунди, тај резултат се може претворити у реалан број сати тако што се израчуна \(h+\frac{m}{60}+\frac{s}{60^2}\) и потребно је одредити вредности \(h\), \(m\) и \(s\) тако да ово буде што ближе могуће вредности \(t\) израчунатој у првом делу задатка.

Гледано другачије, израз \(t_s=h\cdot 60^2+m\cdot 60+s\) представља број секунди у том резултату (он је увек цео број). Са друге стране израз \(t\cdot 60^2\) представља реалан број секунди за који се тркачи сустигну. Та два броја треба да буду што ближа, тако да ћемо целобројну вредност израза \(h\cdot 60^2+m\cdot 60+s\) одредити заокруживањем броја \(t\cdot 60^2\) на најближу целобројну вредност (у имплементацији се ово може урадити коришћењем библиотечке функције round).

Када знамо целобројну вредности израза \(t_s=h\cdot 60^2+m\cdot 60+s\), потребно је одредити његове компоненте \(h\), \(m\) и \(s\). При том мора да важи да је \(0\leq s<60\) и \(0\leq m<60\). Приметимо да се овде ради о запису у бројевној основи \(60\).

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>

int main(void)
{
    double s, v1, v2;
    scanf("%lf%lf%lf", &s, &v1, &v2);
    double  t = s / fabs(v1 - v2);
    int ts = (int)round(t * 3600);
    int sek = ts % 60;
    int min = (ts / 60) % 60;
    int sat = ts / (60 * 60);
    printf("%d\n%d\n%d", sat, min, sek);
    return 0;
}

Радијани

Прочитај текст задатка.

Обим круга \(O\) полупречника \(r\) једнак је \(2r\pi\). Дужина кружног лука \(l\) пропроционална је углу \(\alpha\) (задатом у реалном броју степени) који одговара том луку. Пошто целом кругу одговара угао од \(360^\circ\), важи пропорција \(2r\pi:360^\circ=l:\alpha\) тј. \(l=\frac{\alpha\cdot r\cdot\pi}{180^\circ}\). Радијан је онај угао \(\alpha\) код којег је \(l=r\), па важи да је \(r=\frac{\alpha\cdot r\cdot\pi}{180^\circ}\) тј. \(\alpha=\frac{180^\circ}{\pi}\). Дакле угао \(\mathrm{rad}=\frac{180^\circ}{\pi}\).

Ако је неки угао изражен као \(x\,\mathrm{rad}\), тада је он једнак \(\frac{x\cdot 180^\circ}{\pi}\). Tиме се угао у радијанима може представити реалним бројем степени.

Превођење тог угла у реални број секунди је једноставно (множењем са 60 добије се реални број минута и новим множењем са 60 добија се реални број секунди). Пошто ми желимо да угао изразимо у степенима, минутима и секундама, потребно је да добијемо цео број секунди и потребно је извршити заокруживање (овим кораком се утао може малчице променити, међутим, то је неопходно јер се степенима, минутима и секундама не може представити произвољан угао - најмањи угао који се може представити је секунда која износи 3600-ти део степена). У задатку се тражи заокруживање на најближи угао, тако да ћемо употребити библиотечку функцију round().

Када се добије угао изражен у целом броју секунди, превођење у степене, минуте и секунде вршимо на неки од уобичајених начина, аналогно конверзијама секунди у секунди, минуте и сате. На крају је степене потребно свести на интервал \([0,360)\), што радимо израчунавањем остатка при дељењу са \(360\).

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <stdio.h>
#include <math.h>

int main(void)
{
    double radijani;
    scanf("%lf", &radijani);
    int S = (int)round((radijani / M_PI * 180.0) * 3600);
    int sek = (S / 1) % 60;
    int min = (S / 60) % 60;
    int st = (S / (60 * 60)) % 360;
    printf("%d:%d:%d", st, min, sek);
    return 0;
}

Гпс

Прочитај текст задатка.

Ако је број степени \(st\), минута \(min\), а секунди \(sek\), тада се резултат може добити као \(st+\frac{min}{60}+\frac{sek}{60^2}\).

Наиме, у једном степену има 60 минута тако да један минут представља \(\frac{1}{60}\) степени. Један минут има 60 секунди тако да у степену има \(60^2\) секунди па зато једна секунда представља \(\frac{1}{60^2}\) степени.

Суптилно место у имплементацији је то што су улазни подаци цели бројеви, а резултат треба да буде реалан број. Да би се постигло да се врши реално, а не целобројно дељење, довољно је константе у имениоцу записати као реалне константе (нпр. написаћемо 60.0 уместо 60). Чим је један аргумент у аритметичком изразу реалан, и други ће имплицитно бити конвертован у реалан број.

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void)
{
    int st, min, sek;
    scanf("%d%d%d", &st, &min, &sek);
    printf("%.5lf", st + min / 60.0 + sek / 3600.0);
    return 0;
}

Поклапање казаљки

Прочитај текст задатка.

Ако је \(t\) број који представља минут у сату \(x\) у коме се поклапају сатна и минутна казаљка, тада је:

  • угао минутне казаљке једнак \(t\cdot 6^\circ\), где је \(6^\circ\) број степени за које се минутна казаљка помера по минуту (пошто минутна казаљка обиђе пун круг за 360 минута, то следи из \(\frac{360^\circ}{60}\)),

  • угао сатне казаљке једнак \(x\cdot 30^\circ+t\cdot 0.5^\circ\), где је \(30^\circ\) угао у степенима који представља померање сатне казаљке на сваки сат, а \(0.5^\circ\) померање сатне у текућем сату које је 12 пута мање него померање минутне које је \(t\cdot 6^\circ\).

Из једначине \(x\cdot 30^\circ+t\cdot 0.5^\circ=t\cdot 6^\circ\) добија се да је \(t=\frac{60\cdot x}{11}\).

Пошто је \(x\) цео број, приликом израчунавања израза \(\frac{60\cdot x}{11}\), потребно је константе навести у реалном облику (као 60.0 и 11.0), да не би грешком дошло до целобројног дељења. Друго решење било би да се променљива \(x\) уведе као реална, без обзира на то што су јој вредности увек цели бројеви. Пошто је вредност израза реалан број минута, а као решење задатка се очекује њему најближи цео број минута, потребно је извршити заокруживање коришћењем библиотечке функције round().

Предложено решење задатка

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>

int main(void)
{
    int sat;
    scanf("%d", &sat);
    int min = (int)round(60.0 * sat / 11.0);
    printf("%d", min);
    return 0;
}