[C] Backtracking

Mam do napisania program który na wejściu dostaje ilość elementów, elementy i dzieli elementy na 2 grupy tak, żeby ich suma była równa. jeżeli suma jest nieparzysta ma wypisać nie, jeżeli suma jest parzysta ale największy element jest większy od połowy sumy również ma wypisać. Jeżeli wszystko jest w porządku ma wypisać 2 grupy tak jak napisałem wyżej.

Napisałem już kod, ale działa tylko dla niektórych przykładów. Jeżeli w przykładzie jakieś wartości się powtarzają to program wtedy nie zachowuje się tak jak powinien

int sumowanie(int *tab,int rozmiar)

{

    int suma=0;

    int i;

    for(i=0;i
    {

                          suma+=tab[i];

    }


    return suma;

}


int rozwiazanie(int *tab1,int *tab2,int rozmiar,int koniec,int suma,int pozycja)

{

    for(;pozycja
                           suma+=tab1[pozycja];

                           if (suma == koniec){

                                              tab2[pozycja]=1;

                                              return 2;

                           }

                           if ( suma < koniec ){

                                              tab2[pozycja]=1;

                                              ++pozycja;

                                              rozwiazanie( tab1,tab2,rozmiar,koniec,suma,pozycja );

                           }


                           if( suma > koniec ){


                                             suma-=tab1[pozycja];

                                             tab2[pozycja]=0;

                                             ++pozycja;

                           }



     }

    return -1;

}


int main()

{

    int N;

    scanf("%d",&N);

    int Tablica[100];

    int Wynik[100]={0};

    int i;

    for(i=0;i
    {

                          scanf("%d",&Tablica[i]);

    }

    int suma=sumowanie(Tablica,N);

    if( suma%2 != 0 )

    {

        printf("NIE");

        return 0;

    }



    if( Tablica[0] > suma/2)

    {

        printf("NIE");

        return 0;

    }


    int koniec=suma/2;

    int rob=0;


    i=rozwiazanie(Tablica,Wynik,N,koniec,rob,0);

    if (i == 2)

    {

          for(i=0;i
          {

                                if ( Wynik[i]==1)

                                {

                                     printf("%d ",Tablica[i]);

                                }

          }


          printf("\n");


          for(i=0;i
          {

                                if ( Wynik[i]==0)

                                {

                                     printf("%d ",Tablica[i]);

                                }

          }


          printf("\n");

    }


    if (i == -1)

    {

          printf("NIE");

    }


    return 0;

}

Będę bardzo wdzięczny za pomoc w naprowadzeniu na znalezienie i poprawienie błędu.

znaleznienie dwóch rozłącznych zbiorów o równej sumie ma całkiem sporą złożoność (jeśi się nie mylę to podstawowy algorytm będzie miał 2^n).

Natomiast u Ciebie ma co najwyżej n.

Twierdzisz że nie zachowuje się tak jak powinien kiedy masz powtarzające się elementy.

Załóżmy że masz 6 elementów: 1 2 3 4 5 7

Twój algorytm liczy sumę: 22

koniec = 11;

uruchamia się funkcja roziązanie

pozycja początkowo 0

więc

suma =1 mniej niż koniec

zatem wywołuje rozwiązanie

suma =3 mniej koniec

zatem wywołuje rozwiązanie

suma =6 mniej niż koniec

zatem wywołuje rozwiązanie

suma =10 mniej niż koniec

zatem wywołuje rozwiązanie

suma =15 więcej niż koniec

zatem drugi if

suma = 8 mniej niż koniec

wywołuje rozwiązanie

ale to jest koniec tablicy więc program mówi że nie ma takiego podziału

Czy aby na pewno?

1 3 7; 2 4 5

7 4 ; 1 2 3 5

Dobrze by było zdebugować w jakimś ide ale wydaje mi się że nie popełniłem tutaj błędu.

Zauważyłem, że ten problem jest bardzo popularny. Jakiś czas temu był identyczny http://forum.dobreprogramy.pl/algorytm-nawrotami-t437472.html. Przepisałem swój kod C# (który kiedyś tam napisałem) na C. Ta wersja kodu (podobnie jak Twoja) wyszukuje tylko jedno rozwiązanie. Jeśli potrzebujesz znaleźć wszystkie to będziesz to musiał dopisać sam (chyba, że nie dasz rady, to jak znajdę chwilę to się tym zajmę). I jeszcze jedno … ja na co dzień w C nie piszę, więc musisz obejrzeć ten kod pod kątem tego, czy aby na pewno użyłem najbardziej optymalne konstrukcje programistyczne. Sam algorytm jest ok.

#include 

#include 

#include 


int compareDescendingOrder(const void *x, const void *y)

{

    return (*(int*)y - *(int*)x);

}


int calculateNumbersSum(int *numbers, int numbersCount)

{

    int sum=0;


    int i;

    for(i=0; i
        sum+=numbers[i];


    return sum;

}


bool checkIfNumbersSolutionExists(int numbersCount, int *numbers, int *resultsCount, bool **results, bool** resultsTemporary, int **resultsNumbersTemporary)

{

    bool numbersSolutionExists = false;


    int i;

    int j;

    int k;

    for (i=1; i<=*resultsCount; i++)

    {

        int offset = (i-1) * numbersCount;


        numbersSolutionExists = true;

        k = 0;

        for(j=0; j
        {

            if((*resultsTemporary)[j])

            {

                if (numbers[j] != (*resultsNumbersTemporary)[offset + k])

                {

                    numbersSolutionExists = false;

                    break;

                }

                else

                    k++;

            }

        }


        if(numbersSolutionExists)

            break;

    }


    return numbersSolutionExists;

}


void addNumbersSolution(int numbersCount, int *numbers, int *resultsCount, bool **results, bool** resultsTemporary, int **resultsNumbersTemporary)

{

    *resultsCount += 1;

    *results =(bool *) realloc(*results, *resultsCount * numbersCount * sizeof(bool));

    *resultsNumbersTemporary =(int *) realloc(*resultsNumbersTemporary, *resultsCount * numbersCount * sizeof(int));


    int i;

    int k;

    int offset = (*resultsCount-1) * numbersCount;


    for(i=0, k=0; i
    {

        (*resultsNumbersTemporary)[offset + i] = 0;

        (*results)[offset + i]=(*resultsTemporary)[i];


        if((*resultsTemporary)[i])

        {

            (*resultsNumbersTemporary)[offset + k] = numbers[i];

            k++;

        }

    }

}


void findAllNumbersSolutionHelper(int currentIndex, int currentSum, int searchedSum, int numbersCount, int *numbers, int *resultsCount, bool **results, bool** resultsTemporary, int **resultsNumbersTemporary)

{

    bool goFurther = false;

    currentSum += numbers[currentIndex];

    (*resultsTemporary)[currentIndex] = true;


    if (currentSum == searchedSum && !checkIfNumbersSolutionExists(numbersCount, numbers, resultsCount, results, resultsTemporary, resultsNumbersTemporary))

        addNumbersSolution(numbersCount, numbers, resultsCount, results, resultsTemporary, resultsNumbersTemporary);

    else if (currentSum < searchedSum)

        goFurther = true;


    if (goFurther)

    {

        int i;

        for (i = 1; i < numbersCount; i++)

            if (!(*resultsTemporary)[i])

                findAllNumbersSolutionHelper(i, currentSum, searchedSum, numbersCount, numbers, resultsCount, results, resultsTemporary, resultsNumbersTemporary);

    }


    (*resultsTemporary)[currentIndex] = false;

}


void findAllNumbersSolution(int searchedSum, int numbersCount, int *numbers, int *resultsCount, bool **results)

{

    bool *resultsTemporary;

    resultsTemporary = (bool *) malloc(numbersCount*sizeof(bool));


    int *resultsNumbersTemporary;

    resultsNumbersTemporary = (int *) malloc(numbersCount*sizeof(int));


    int i;

    for(i=0; i
        resultsTemporary[i] = false;


    findAllNumbersSolutionHelper(0, 0, searchedSum, numbersCount, numbers, resultsCount, results, &resultsTemporary, &resultsNumbersTemporary);


    free(resultsTemporary);

    free(resultsNumbersTemporary);

}


int main()

{

    int numbersCount;

    // Get numbers count

    scanf("%d",&numbersCount);


    int *numbers;

    numbers=(int *) malloc(numbersCount*sizeof(int));

    // Get numbers

    int i;

    for(i=0; i
        scanf("%d",&numbers[i]);


    int numbersSum=calculateNumbersSum(numbers,numbersCount);

    if( numbersSum%2 != 0 )

    {

        printf("Suma podanych liczb jest nieparzysta. Zadanie nie ma rozwiazania.");

        free(numbers);

        return 0;

    }


    qsort(numbers, numbersCount, sizeof(int), compareDescendingOrder);

    if( numbers[0] > numbersSum/2)

    {

        printf("Najwieksza z liczb jest wieksza niz polowa sumy wszystkich podanych liczb. Zadanie nie ma rozwiazania.");

        free(numbers);

        return 0;

    }


    int resultsCount = 0;

    bool *results;

    results = (bool *) malloc(numbersCount*sizeof(bool));


    findAllNumbersSolution(numbersSum / 2, numbersCount, numbers, &resultsCount, &results);


    if (resultsCount > 0)

    {

        printf("Ilosc rozwiazan: %d \n",resultsCount);


        for (i=1; i<=resultsCount; i++)

        {

            printf("---------------------------------------------------------\n");


            int offset = (i-1) * numbersCount;

            int j;


            for(j=0; j
                if (results[offset + j])

                    printf("%d ",numbers[j]);


            printf("\n");


            for(j=0; j
                if (!results[offset + j])

                    printf("%d ",numbers[j]);


            printf("\n");

        }

    }

    else printf("Nie istnieje taka kombinacja liczb, ktorych suma jest rowna polowie sumy wszystkich podanych liczb. Zadanie nie ma rozwiazania.");


    free(numbers);

    free(results);


    return 0;

}

EDIT: Dodałem, żeby wyszukiwało wszystkie rozwiązania :slight_smile:

grzelix, elementy są ustawione nie rosnąco

np dla danych 13 8 5 4 3 1 dziala jak należy

ale juz dla 12 4 4 4 3 3 2 2 nie działa

a dla 9 7 6 5 3 ?

to że elementy są uporządkowane jest ważnym elementem tego zadania.

Wyjście:

9 6

7 5 3

ok widzę teraz dokładnie jak działa ten algorytm

w tej chwili nie mogę znaleźć przykładu bez powtarzających się liczb ale jestem prawie pewien że taki istnieje.

Natomiast do tego przykładu co podałeś:

12 4 4 4 3 3 2 2

algorytm zsmuje 3 pierwsze elementy - uzyska wtedy 16 i każdy kolejny będzie pomijał bo suma będzie za duża.

i to chyba jasne że inaczej to działać nie może czyli algorytm posiada skazę.

Właśnie i wydaje mi się, że problem jest z wywołaniem funkcji. Powinna się wywoływać, usuwać zaznaczenie elementu, usuwać go z sumy i zaczynać od pozycji +1.