Cod sursa(job #440285)

Utilizator skeletAndrei Casian skelet Data 11 aprilie 2010 23:30:00
Problema Gutui Scor 20
Compilator c Status done
Runda teme_upb Marime 6.93 kb
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#define bool char

#define INFILE  "gutui.in"
#define OUTFILE "gutui.out"

/* #define DEBUG */

#define SCRIE_EROARE(eroare) fprintf( stderr, "Eroare in %s: %s\n", __func__, eroare );

/* Structura de gutuie */
typedef struct
{
    int i_imax;
    int i_greutate;
    bool b_luata;
} gutuie_t;

/* Structura de pereche de gutui */
typedef struct
{
    gutuie_t *p_gutuie0;
    gutuie_t *p_gutuie1;
    bool b_valida;
} pereche_gutui_t;

/* Vrem sa sortam crescator dupa i_imax si descrescator dupa i_greutate */
int gutuie_compara( gutuie_t *una, gutuie_t *alta )
{
    if( una->i_imax < alta->i_imax )
        return -1;
    if( una->i_imax > alta->i_imax )
        return +1;
    if( una->i_greutate > alta->i_greutate )
        return -1;
    if( una->i_greutate < alta->i_greutate )
        return +1;
    return 0;
}

gutuie_t *gutui_merge( int i_n1, gutuie_t *p_gutui1, int i_n2, gutuie_t *p_gutui2 )
{
    gutuie_t *p_gutui3 = calloc( i_n1 + i_n2, sizeof(gutuie_t) );
    int i1, i2, i;

    for( i = i1 = i2 = 0; i1 < i_n1 && i2 < i_n2; ++ i )
        p_gutui3[i] = gutuie_compara( p_gutui1 + i1, p_gutui2 + i2 ) < 0 ? p_gutui1[i1 ++] : p_gutui2[i2 ++];

    while( i1 < i_n1 )
        p_gutui3[i ++] = p_gutui1[i1 ++];
    while( i2 < i_n2 )
        p_gutui3[i ++] = p_gutui2[i2 ++];

    if( i != i_n1 + i_n2 )
        SCRIE_EROARE( "wtf, intorc mai putine elemente decat trebuie" )

    return p_gutui3;
}

void gutui_merge_sort( int i_ngutui, gutuie_t *p_gutui )
{
    if( i_ngutui <= 1 )
        return;

    int i_n1 = i_ngutui / 2;
    int i_n2 = i_ngutui - i_n1;
    gutuie_t *p_jumate1 = p_gutui;
    gutuie_t *p_jumate2 = p_gutui + i_n1;

    gutui_merge_sort( i_n1, p_jumate1 );
    gutui_merge_sort( i_n2, p_jumate2 );

    gutuie_t *p_sortate = gutui_merge( i_n1, p_jumate1, i_n2, p_jumate2 );
    memcpy( p_gutui, p_sortate, i_ngutui * sizeof(gutuie_t) );
    free( p_sortate );
}

#ifdef DEBUG
void gutui_afiseaza( int i_n, gutuie_t *p_gutui )
{
    int i;
    for( i = 0; i < i_n; ++ i )
        printf( "(%d, %d, %d); ", p_gutui[i].i_imax, p_gutui[i].i_greutate, p_gutui[i].b_luata );
    printf( "\n" );
}
#endif

int gutui_aduna_greutati( int i_n, gutuie_t *p_gutui )
{
    int s = 0;
    gutuie_t *p_last = p_gutui + i_n;
    while( p_gutui < p_last )
    {
        if( !p_gutui->b_luata )
        {
            s += p_gutui->i_greutate;
            p_gutui->b_luata = 1;
        }

        p_gutui ++;
    }

    return s;
}

pereche_gutui_t gutui_swap_optim( int i_n, gutuie_t *p_gutui )
{
    /*
    Trebuie gasita gutuile care au diferenta maxima de greutate si e voie sa se
    ia a doua in locul primei, iar prima a fost luata si a doua nu
    */

    int i0, i1;
    int i_pmax0 = -1, i_pmax1 = -1, i_maxdiff = 0;
    pereche_gutui_t pereche;
    pereche.b_valida = 0;

    for( i0 = 0; i0 < i_n; ++ i0 )
        if( p_gutui[i0].b_luata )
            for (i1 = i0; i1 < i_n; ++ i1 )
                if( !p_gutui[i1].b_luata )
                    if ( p_gutui[i1].i_greutate - p_gutui[i0].i_greutate > i_maxdiff
                        || ( p_gutui[i1].i_greutate - p_gutui[i0].i_greutate == i_maxdiff
                        && ( i1 - i0 < i_pmax1 - i_pmax0 ) ) )
                    {
                        i_pmax0 = i0;
                        i_pmax1 = i1;
                        i_maxdiff = p_gutui[i1].i_greutate - p_gutui[i0].i_greutate;
                    }

    if( i_pmax0 > -1 && i_pmax1 > -1 && i_maxdiff > 0 )
    {
        #ifdef DEBUG
        printf( "Dau la swap %d cu %d, diferenta %d\n", i_pmax0, i_pmax1, i_maxdiff );
        #endif
        pereche.p_gutuie0 = p_gutui + i_pmax0;
        pereche.p_gutuie1 = p_gutui + i_pmax1;
        pereche.b_valida = 1;
    }

    return pereche;
}

int gutui_calculeaza_greutate_neoptima( int i_n, gutuie_t *p_gutui )
{
    if( i_n <= 0 )
        return 0;

    int i_last_imax = -1;
    int i_luate = 0, i_calup = 0;
    int i_disponibile = 0;
    int i;
    int s = 0;
    gutuie_t *p_calup = p_gutui;

    for( i = 0; i < i_n; ++ i )
    {
        if( p_gutui[i].b_luata )
            continue;

        i_disponibile += p_gutui[i].i_imax - i_last_imax;
        #ifdef DEBUG
        printf( "%d inceput -> disp = %d, luate = %d, icalup = %d, suma = %d\n", i, i_disponibile, i_luate, i_calup, s );
        #endif

        if( p_gutui[i].i_imax > i_last_imax )
        {
            p_calup = p_gutui + i;
            i_calup = i;
        }

        while( i_disponibile > i - i_calup )
        {
            int i_luabile = i_disponibile - i + i_calup;
            s += gutui_aduna_greutati( i_luabile, p_calup );

            i_disponibile -= i_luabile;
            i_luate += i_luabile;
        }

        i_last_imax = p_gutui[i].i_imax;

        #ifdef DEBUG
        printf( "%d sfarsit -> disp = %d, luate = %d, icalup = %d, suma = %d\n", i, i_disponibile, i_luate, i_calup, s );
        #endif
    }

    /* Vezi cate gutui mai sunt ramase si pot fi luate, din ultimul calup */
    int i_ramase = i_last_imax - i_luate + 1;
    if( i_ramase > i_n - i_last_imax )
        i_ramase = i_n - i_last_imax;
    s += gutui_aduna_greutati( i_ramase, p_calup );
    #ifdef DEBUG
    printf( "suma = %d\n", s );
    gutui_afiseaza( i_n, p_gutui );
    #endif

    /* Trebuie facute cateva schimbari pentru optimizarea sumei */
    pereche_gutui_t pereche;
    pereche.b_valida = 0;
    pereche.p_gutuie0 = pereche.p_gutuie1 = NULL;

    while( (pereche = gutui_swap_optim( i_n, p_gutui )).b_valida )
    {
        /* Efectueaza interschimbarea de sacrificiu */
        pereche.p_gutuie0->b_luata = 0;
        pereche.p_gutuie1->b_luata = 1;
        s += pereche.p_gutuie1->i_greutate - pereche.p_gutuie0->i_greutate;
    }

    #ifdef DEBUG
    printf( "Dupa optimizari, suma = %d\n", s );
    gutui_afiseaza( i_n, p_gutui );
    #endif

    return s;
}

int main( int argc, char *argv[] )
{
    int i_n = 0, i_h = 0, i_u = 0;

    /* Citeste datele de intrare */
    FILE *fin = fopen( INFILE, "r" );
    if( !fin )
    {
        SCRIE_EROARE( "Nu pot deschide fisierul de intrare!" )
        return 2;
    }

    fscanf( fin, "%d %d %d\n", &i_n, &i_h, &i_u );
    gutuie_t *p_gutui = calloc( i_n, sizeof(gutuie_t) );

    if( i_u == 0 )
        SCRIE_EROARE( "Inaltimea la care se ridica gutuile este 0!" )

    int i, i_inaltime;
    for( i = 0; i < i_n; ++ i )
    {
        /* Pune o gutuie in lista */
        fscanf( fin, "%d %d", &i_inaltime, &p_gutui[i].i_greutate );
        p_gutui[i].i_imax = i_u != 0 ? (i_h - i_inaltime) / i_u : INT_MAX - 1;
    }

    /* Sorteaza gutuile */
    #ifdef DEBUG
    gutui_afiseaza( i_n, p_gutui );
    #endif
    gutui_merge_sort( i_n, p_gutui );
    #ifdef DEBUG
    gutui_afiseaza( i_n, p_gutui );
    #endif

    /* Vezi greutatea maxima */
    int i_gmax = gutui_calculeaza_greutate_neoptima( i_n, p_gutui );

    free( p_gutui );

    /* Scrie rezultate */
    FILE *fout = fopen( OUTFILE, "w" );
    fprintf( fout, "%d", i_gmax );
    fclose( fout );
    
    return 0;
}