Zadanie z ilością kroków dzielenia i dodawania

0

Alicja ma dwa magiczne grzyby. Po zjedzeniu kawałka pierwszego robi się
dwa razy wyższa, a po zjedzeniu kawałka drugiego - kurczy się o 1 m
Przyklad: alicja ma 115 cm a chce miec 120 cm
Prawidłowe rozwiązanie: Alicja najpierw powinna zjeść kawałek drugiego grzyba, po którym
będzie mierzyła 15 cm, a następnie trzy kawałki pierwszego grzyba, po których będzie mierzyła odpowiednio 30 cm, 60 cm i 120 cm. Razem Alicja musi zjeść cztery kawałki grzybów.

Napisz program, który rozwiązuje problem ogólniejszy: jaka jest najmniejsza liczba kawałków grzybów, które musi zjeść Alicja, aby zmienić wzrost z początkowego x cm na końcowe y cm. Działanie grzybów jest zgodne z oryginalnym zadaniem, natomiast każdy grzyb składa
się tylko z ośmiu kawałków. Może się więc zdarzyć, że oczekiwana zmiana wzrostu będzie
niewykonalna.

wzrost ujemny nie jest mozliwy

proszę pomóżcie

2
  1. Jakiej pomocy potrzebujesz? Z czym sobie nie radzisz?
  2. W tytule masz C++, w tagach C — w jakim języku piszesz?
3

Przez "pomoc" ja rozumiem wsparcie kogoś w działaniu. Co zrobiłeś poza napisaniem tego pytania na forum.
Chętie pomogę, ale z konkretnym problemem w jakimś kodzie, a nie będe za kogoś robił zadanie na zliczanie programowania na studiach.

Jeśli nie poprawisz pytania (pokażesz własny kod do poprawienia), moderatorzy z satysfakcją zamkną ci to pytanie (czyli ja albo Althorion).

0
#include <iostream>

using namespace std;

int main()
{
    int wzrost;
    int w;
    int testy;
    cin >> testy;
    for (int i = 0; i < testy; i++)
    {
        int grzyb1 = 0;
        int grzyb2 = 0;
        int roznica1 = 0;
        int roznica2 = 0;
        int a = 1;
        cin >> wzrost >> w;
        if (wzrost == w)
        {
            cout << "0" << endl;
        }
        else
        {
            while (wzrost != w && (grzyb1 < 8 || grzyb2 < 8))
            {
                while (wzrost <= 100 && w != wzrost && grzyb2 < 8)
                {
                    wzrost = wzrost * 2;
                    grzyb2 = grzyb2 + 1;
                }
                if (wzrost % 5 == 0 && w % wzrost != 0 && wzrost > 100)
                {
                    while (w % wzrost != 0)
                    {
                        if ((w % (wzrost - 100 * a)) == 0)
                        {
                            wzrost = wzrost - 100 * a;
                            grzyb1 = grzyb1 + a;
                        }
                        a = a + 1;
                    }
                }
                if (wzrost != w)
                {
                    roznica1 = wzrost - 100;
                    roznica2 = wzrost * 2;
                    if (roznica1 == w)
                    {
                        roznica1 = 0;
                    }
                    if (roznica2 == w)
                    {
                        roznica2 = 0;
                    }
                    if (roznica1 != 0)
                    {
                        if (w > roznica1)
                        {
                            roznica1 = w - roznica1;
                        }
                        else
                        {
                            roznica1 = roznica1 - w;
                        }
                    }
                    if(roznica2!=0)
                    {
                        if (w > roznica2)
                        {
                            roznica2 = w - roznica2;
                        }
                        else
                        {
                            roznica2 = roznica2 - w;
                        }                   
                    }
                    
                    if (roznica1 > roznica2)
                    {
                        
                            wzrost = wzrost * 2;
                            grzyb2 = grzyb2 + 1;
                       
                    }
                    else
                    {
                        wzrost = wzrost - 100;
                        grzyb1 = grzyb1 + 1;
                    }
                }
            }
        }
        if (grzyb1 > 8 || grzyb2 > 8)
        {
            cout << "NIE" << endl;
        }
        else
        {
            cout << grzyb1 + grzyb2<<endl;
        }
    }
    return 0;
    }

to moj kod wyskakuje mi blad if (wzrost % 5 == 0 && w % wzrost != 0 && wzrost > 100) przy wpisywaniu wzrostu 100 i w 900 zrobilem ja bo nie dzialal mi program przy liczbach podzielnych przez 5 przynajmniej nie przy kazdej pisze w jezyku c++ , przepraszam za wczesniejsze niescisłosci w pytaniu

0

a nie prościej zamiast drabinki if generować kolejne możliwe kombinacje i tylko sprawdzić wynik ?
Bo jak mam wybierać pomiędzy nieczytelnym kodem a ilością cykli procesora to ja wole aby kod był czytelny

0
Jakub Oguras napisał(a):

to moj kod wyskakuje mi blad if (wzrost % 5 == 0 && w % wzrost != 0 && wzrost > 100) przy wpisywaniu wzrostu 100 i w 900 zrobilem ja bo nie dzialal mi program przy liczbach podzielnych przez 5 przynajmniej nie przy kazdej pisze w jezyku c++ , przepraszam za wczesniejsze niescisłosci w pytaniu

Nie wiem, czy jestem jedyny, ale za nic nie rozumiem tego zdania…

A co do problemu, to Marius.Maximus ma rację — tutaj trzeba przemyśleć sposób rozwiązania i napisać kod na nowo. Najłatwiej, wg mnie, do tego zadania podejść od tyłu — żeby Alicja skończyła z dokładnie y centymetrami wzrostu, musi mieć krok wcześniej albo 0.5 * y centymetrów wzrostu (po czym zjeść grzybek, który podwoi jej wzrost), albo y + 100 centymetrów wzrostu (po czym zjeść grzybek, który ją skurczy o metr). I tak można rekurencyjnie.

0

Ja bym zrobił to tak.

Ponieważ grzybów jest dwa, a każdego z nich można zjeść po 8 kawałków, to znaczy, że razem jest 16 kawałków.

Ponieważ są dwa grzyby, to oznaczmy pierwszy grzyb przez 0, drugi przez 1.

Potrzebujemy więc maksymalnie 16-elementowy ciąg zer i jedynek.

Ponieważ komputery już zapisują dane w postaci ciągu zer i jedynek, możemy to wykorzystać i pobawić się operacjami bitowymi.

  1. iterujemy przez wszystkie liczby od 0 do 2^16 - 1 (65536 możliwości). Każda liczba i reprezentuje ciąg bitów potencjalnego rozwiązania
  2. każdą liczbę i potraktujemy jako ciąg bitów, iterując przez bity, nakładamy pierwszą albo drugą operację(grzyb)
  3. jak znajdziemy pożądany wzrost, to przerywamy pętlę i zapisujemy jeżeli liczba kroków jest mniejsza niż wcześniej

Coś takiego wymyśliłem w JS, ale można by przepisać na C++.

const ops = [
	x => x * 2,
	x => x - 100,
];

const pieces = 16;
function find(currentHeight, desiredHeight) {
	const min = {
		count: Infinity,
		text: '',
	};

	for (let i = 0; i < 2**pieces; i++) {
		let height = currentHeight;
		let s = '';
		for (let bit = 0; bit < pieces; bit++) {
			const op = (i & (1 << bit)) >> bit; 
			height = ops[op](height);
			if (op == 0) s += 'first\n';
			if (op == 1) s += 'second\n';
			if (height == desiredHeight && bit < min.count) {
				min.text = s;
				min.count = bit;
				break;
			}
		}
	}
	return min;
}
const min = find(115, 120);

console.log(`== Count: ${min.count}\n== Steps: \n${min.text}`);

No i odnośnie może mglistego fragmentu:

const op = (i & (1 << bit)) >> bit;

1 << bit - bo iterujemy przez kolejny bit, więc przesuwamy jedynkę w lewo np. 1 << 3 da nam binarnie 1000
(i & (1 << bit)) robimy operację AND porównując z maską bitową liczby i, która reprezentuje całe rozwiązanie
i całość robimy >> bit żeby z powrotem zawrócić ten bit (zgaszony bądź zapalony) i żeby wyszło zero albo jeden

mamy więc nasz numer grzyba op, którego możemy użyć jako indeks dwuelementowej tablicy ops.

0
LukeJL napisał(a):

Ponieważ komputery już zapisują dane w postaci ciągu zer i jedynek, możemy to wykorzystać i pobawić się operacjami bitowymi.

  1. iterujemy przez wszystkie liczby od 0 do 2^16 - 1 (65536 możliwości). Każda liczba i reprezentuje ciąg bitów potencjalnego rozwiązania
  2. każdą liczbę i potraktujemy jako ciąg bitów, iterując przez bity, nakładamy pierwszą albo drugą operację(grzyb)
  3. jak znajdziemy pożądany wzrost, to przerywamy pętlę i zapisujemy jeżeli liczba kroków jest mniejsza niż wcześniej

Widzę w tym dwa problemy:

  1. Ciąg operacji 1 → 2 → 4 → 8 → 7 → 6 prowadzi do tego samego skutku, co ciąg 1 → 2 → 4 → 3 → 6, a jest dłuższy — jak zapewnić to, że krótszy ciąg zostanie odnaleziony jako pierwszy?
  2. Potrzebujemy sprawdzić nie 2¹⁶ ciągów (tzn. ciągów o długości dokładnie 16), tylko 2¹⁷ ciągów (tzn. ciągów o długości co najwyżej 16). Wciąż, 131 072 możliwości to na tyle mało, że da się w sensownym czasie wygenerować w czasie kompilacji.
0
Althorion napisał(a):
  1. Potrzebujemy sprawdzić nie 2¹⁶ ciągów (tzn. ciągów o długości dokładnie 16), tylko 2¹⁷ ciągów (tzn. ciągów o długości co najwyżej 16). Wciąż, 131 072 możliwości to na tyle mało, że da się w sensownym czasie wygenerować w czasie kompilacji.

Explain. Ja to widzę, że jest 2^16 ciągów, tylko że nie wszystkie muszę przelatywać do końca.

  1. Ciąg operacji 1 → 2 → 4 → 8 → 7 → 6 prowadzi do tego samego skutku, co ciąg 1 → 2 → 4 → 3 → 6, a jest dłuższy — jak zapewnić to, że krótszy ciąg zostanie odnaleziony jako pierwszy?

Nie musi, ważne że finalnie krótszy ciąg zostanie znaleziony. No chyba że dla celów optymalizacji (która mogłaby być potrzebna, gdyby było więcej kawałków grzybów albo miało to działać w pętli. Ale zadanie nic o tym nie wspomina).

Tym niemniej tak na szybko, to żeby zoptymalizować, to pewnie bym odwrócił "na lewą stronę" te pętlę - najpierw iterowałbym po bitach, a w środku sprawdzał kolejne rozwiązania. Tylko wtedy potrzebowałbym dodatkowej tablicy na wysokości. Coś jak (pseudokod)

heights = Array(2^16)
heights.fill(initialHeight)

for bit in 0..16:
  for solution in 0..2^16:
    heights[solution] = eatMushroom(solution, bit)
    if heights[solution] == desiredHeight:
      return (solution, count)

albo też potraktować to jako graf i zrobić szukanie drogi algorytmem Dijkstra, BFS itp.

1

Dzielisz przez zero w tym miejscu: w % wzrost

if (wzrost % 5 == 0 && w % wzrost != 0 && wzrost > 100) {

Bo z odpowiednimi danymi wejściowymi pętle poniżej doprowadzą do wzrost = 0.
https://godbolt.org/z/31965zY74

/app/example.cpp:19:38: runtime error: division by zero
0

Swoją drogą, czy tego się nie da policzyć matematycznie?
Ułożyć jakieś równanie/układ równań?
Ktoś wie/ma pomysł, czy coś takiego byłoby możliwe?

0

w jaki sposob

LukeJL napisał(a):

Ja bym zrobił to tak.

Ponieważ grzybów jest dwa, a każdego z nich można zjeść po 8 kawałków, to znaczy, że razem jest 16 kawałków.

Ponieważ są dwa grzyby, to oznaczmy pierwszy grzyb przez 0, drugi przez 1.

Potrzebujemy więc maksymalnie 16-elementowy ciąg zer i jedynek.

Ponieważ komputery już zapisują dane w postaci ciągu zer i jedynek, możemy to wykorzystać i pobawić się operacjami bitowymi.

  1. iterujemy przez wszystkie liczby od 0 do 2^16 - 1 (65536 możliwości). Każda liczba i reprezentuje ciąg bitów potencjalnego rozwiązania
  2. każdą liczbę i potraktujemy jako ciąg bitów, iterując przez bity, nakładamy pierwszą albo drugą operację(grzyb)
  3. jak znajdziemy pożądany wzrost, to przerywamy pętlę i zapisujemy jeżeli liczba kroków jest mniejsza niż wcześniej

Coś takiego wymyśliłem w JS, ale można by przepisać na C++.

const ops = [
	x => x * 2,
	x => x - 100,
];

const pieces = 16;
function find(currentHeight, desiredHeight) {
	const min = {
		count: Infinity,
		text: '',
	};

	for (let i = 0; i < 2**pieces; i++) {
		let height = currentHeight;
		let s = '';
		for (let bit = 0; bit < pieces; bit++) {
			const op = (i & (1 << bit)) >> bit; 
			height = ops[op](height);
			if (op == 0) s += 'first\n';
			if (op == 1) s += 'second\n';
			if (height == desiredHeight && bit < min.count) {
				min.text = s;
				min.count = bit;
				break;
			}
		}
	}
	return min;
}
const min = find(115, 120);

console.log(`== Count: ${min.count}\n== Steps: \n${min.text}`);

No i odnośnie może mglistego fragmentu:

const op = (i & (1 << bit)) >> bit;

1 << bit - bo iterujemy przez kolejny bit, więc przesuwamy jedynkę w lewo np. 1 << 3 da nam binarnie 1000
(i & (1 << bit)) robimy operację AND porównując z maską bitową liczby i, która reprezentuje całe rozwiązanie
i całość robimy >> bit żeby z powrotem zawrócić ten bit (zgaszony bądź zapalony) i żeby wyszło zero albo jeden

mamy więc nasz numer grzyba op, którego możemy użyć jako indeks dwuelementowej tablicy ops.

wytlumaczysz na jakiej podstawie program decyduje o tym czy zwiekszyc nasz wzrost czy zmniejszyc, rozumiem ze to operacje na bitach ale dlaczego gdy op=0 zwiekszamy a gdy op=1 zmniejszamy. W jaki sposob program oblicza ta liczbe operacji , jakiś prosty przyklad moze by mi pomogl to zrozumiec

jesli dobrze rozumiem program szuka liczby "i" przekonwertowanej na binarna ktora poprzez kombinacje 0 i 1 dotrze w najkrótszym czasie do danego wzrostu ?

1
Jakub Oguras napisał(a):

wytlumaczysz na jakiej podstawie program decyduje o tym czy zwiekszyc nasz wzrost czy zmniejszyc, rozumiem ze to operacje na bitach ale dlaczego gdy op=0 zwiekszamy a gdy op=1 zmniejszamy. W jaki sposob program oblicza ta liczbe operacji , jakiś prosty przyklad moze by mi pomogl to zrozumiec

jesli dobrze rozumiem program szuka liczby "i" przekonwertowanej na binarna ktora poprzez kombinacje 0 i 1 dotrze w najkrótszym czasie do danego wzrostu ?

Odwrotnie. To nie liczba jest przekonwertowana na binarną, tylko układamy (koncepcyjnie! Zanim napiszemy choćby linijkę kodu) z ciągu jedynek i zer liczbę i. Natomiast w kodzie lecimy przez kolejne liczby i i dopiero odkodujemy to, co sobie wymyśliliśmy.

Np. załóżmy, że chcemy znaleźć 3-literowy ciąg znaków, który daje nazwę zwierzęcia. Możemy przelecieć przez cały alfabet
i = AAA
i = AAB
(... ileś iteracji dalej...)
i = BAK
(...)
i = BYK (o, byk!!!!)
(...)
i = EMT
i = EMU (zwierzę!)
(...)
i = KAB
i = KAC
i = KAD
(...)
i = KOB
i = KOC
(...)
i = KOT (kolejne zwierzę)

wyobraź sobie, że zamiast liter mamy zera i jedynki.

ale dlaczego gdy op=0 zwiekszamy a gdy op=1 zmniejszamy.

Tak wynika z przyjętych przeze mnie założeń: Ponieważ są dwa grzyby, to oznaczmy pierwszy grzyb przez 0, drugi przez 1.

Każda liczba i reprezentuje pewien ciąg zakodowanych już wcześniej operacji (zanim w ogóle napiszemy jakąkolwiek linijkę kodu, to już na wstępie założyłem, że liczba 19, czyli binarnie 10011 będzie reprezentować ciąg (czytany od prawej) Drugi Drugi Pierwszy Pierwszy Drugi)

1

Osobiście nie rozumiem jaki jest twój pomysł na rozwiązanie i nie chcę tego analizować.
Tu jest wersja brutal force (przy limicie 16 kroków to nie duży problem):

using AliceStates = std::set<
    std::pair< 
        /* hight */ int,  
        /* shrinking shrooms eaten */ int>>;

bool hasTarget(const AliceStates& s, int target)
{
    auto it = s.lower_bound(std::pair{target, 0});
    return it != s.end() && it->first == target;
}

int alice_minimum_mashroom_pieces(int hight, int target)
{
    static constexpr int shroomsLimit = 8;
    AliceStates  states;
    states.emplace(hight, 0);
    int steps = 0;

    while (!hasTarget(states, target) && !states.empty()) {
        AliceStates nextStepStates;
        for (auto [h, shrinkCount] : states) {
            if (steps - shrinkCount < shroomsLimit) {
                nextStepStates.emplace(h * 2, shrinkCount);
            }
            if (h > 100 && shrinkCount < shroomsLimit) { 
                nextStepStates.emplace(h - 100, shrinkCount + 1);
            }
        }
        ++steps;
        std::swap(states, nextStepStates);
    }

    return states.empty() ? -1 : steps;
}

https://godbolt.org/z/ooE5T6jYT

Szukając lepszego rozwiązania, można użyć tego kodu, by weryfikować dane testowe, a następnie zweryfikowane dane testowe zastosować na nowym szybszym algorytmie.

0
LukeJL napisał(a):

Swoją drogą, czy tego się nie da policzyć matematycznie?
Ułożyć jakieś równanie/układ równań?
Ktoś wie/ma pomysł, czy coś takiego byłoby możliwe?

Hmm da się zrobić coś takiego, że wyliczyć cykl ile trwa, da się to stricte wyliczyć lub można w słowniku cachować wystąpienia i jeśli na trafimy ponownie na ten sam to mamy odległość cyklu.

Potem sprawdzamy pierwszą liczbę i jak jest większa lub równa jaką mamy osiągnąć to znaleźliśmy, jak nie to przesuwamy się o cykl względem pierwszego rozwiązania np. zjedzenie 3 następnych grzybków.

Jak już znajdziemy usuwamy jedności i setki, dzielimy przez 100.
Otrzymujemy ilość stów robimy logarytm z dwóch.
Przykładowo będziemy mieli 1920 cm a określony wzrost ma być 1020cm, czyli mamy o 900 za dużo, musimy znaleźć grzybki kurczące się.
900 / 100 = 9, logarytm 2 z 9, to będzie ~3,coś parę, zaokrąglamy w dół i mamy 3.
2^3 = 8 czyli te 800 odejmujemy od 900 czyli zostaje potem jeszcze 100.
Czyli teraz musimy tego grzybka wsadzić przed 3 od końca rosnącym grzybkiem bo każdy zwiększa o 2.
Musimy też się upewnić, że w danym miejscu można odjąć jeśli już tam odjęliśmy grzybkiem 100 lub nie było tam tyle, to wtedy można zejść z 3 do 2 itp.
Na końcu jak damy grzybek kurczący to będzie -100, przed ostatnim grzybkiem rosnącym to -200, przed przed to -400, n-3 będzie -800
Mamy takie rosnące, rosnące, rosnące, rosnące, rosnące i wkładamy przed 3 od końca jeden(co da -800(-100*2*2*2) i jeden na końcu (-100)
czyli np. rosnący, rosnący, kurczący, rosnący, rosnący, rosnący, kurczący rozwiązanie.

0
Autysta napisał(a):

można w słowniku cachować wystąpienia

Brzmi jak programowanie dynamiczne.

0

"Oguras" chciał Aśkę grzybami zadławić.

1 użytkowników online, w tym zalogowanych: 0, gości: 1