#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;
}