Cod sursa(job #1067556)

Utilizator repp4raduRadu-Andrei Szasz repp4radu Data 26 decembrie 2013 23:58:36
Problema Ghiozdan Scor 100
Compilator cpp Status done
Runda Arhiva de probleme Marime 3.88 kb
/* cea mai comentata sursa din repertoriul personal */
#include <iostream>
#include <cstdio>
#include <deque>

using namespace std;

/* palindrom FTW */
const int MAX = 75057;
const int GMAX = 205;
const int INF = 0x3f3f3f3f;

int N, G, cnt[GMAX];
int dp[2][MAX], D[2][MAX];

deque<int> dq;

void citire() {
    freopen("ghiozdan.in", "r", stdin);
    freopen("ghiozdan.out", "w", stdout);

    cin >> N >> G;

    for(int i = 0, A; i < N; i++) {
        cin >> A;
        cnt[A]++;
    }
}

/* vreau sa rezolv o subproblema a problemei initiale */
/* vreau sa obtin cu obiecte care au greutatile cuprinse intre L si R o greutate totala cat mai apropiata de G */
/* seems legit pana aici... congrats Radu! WOOHOO */
void solve(int L, int R, int G) {
    /* daca nu am ce greutate sa obtin, why bother? */
    //cerr << L << " " << R << " " << G << "\n";
    if(!G) return;

    /* daca am o singura valoare din care sa aleg, apai logic ca aleg valoarea aia... d'oh */
    if(L == R) {
        //cerr << "Entered " << L << " EQUALS " << R << "\n";
        /* si o tot scad atata cat pot :D */
        for(; cnt[L] && G - L >= 0; cnt[L]--, G -= L)
            cout << L << "\n";
        /* dupa care ies din functie ca "My work here is done" */
        return;
    }

    /* the real deal */

    /* O sa vreau sa imi impart problema in doua subprobleme. Una in care dispun de greutatile L...M si alta in care dispun de greutatile M + 1...R */
    int M = (L + R) >> 1, now = 0;

    /* reinitializare dinamica */
    /* dp[i][j] = numarul minim de obiecte de greutate mai mica sau egala cu i de care am nevoie pentru a obtine greutatea j */
    dp[0][0] = 0;
    for(int i = 1; i <= G; i++) 
        dp[0][i] = INF;

    /* acu fac dinamica in complexitate O(200 * G) */
    for(int i = L; i <= R; i++) {
        /* Daca nu e, nu e */
        if(!cnt[i]) continue;
        now = 1 - now;
        
        /* rezolv separat pentru fiecare categorie de resturi la impartirea cu i */
        for(int R = 0; R < i; R++) {
            while(!dq.empty()) dq.pop_front();

            /* parcurg toate variantele posibile cu rest R la impartirea cu i */
            for(int j = R, step = 0; j <= G; j += i, step++) {
                /* classical deque shit */
                int Val = dp[1 - now][j] - step; 
                while(!dq.empty() && dp[1 - now][ dq.back() ] >= Val) dq.pop_back();
                dq.push_back(j); 

                /* modific valoarea lui dp[1 - now][j] pentru ca altfel step ar influenta rezultatul final si nu e ok... */
                dp[1 - now][j] = Val;

                dp[now][j] = dp[1 - now][ dq.front() ] + step;
                
                /* prima aparitie a celei de-a doua dinamici :) */
                /* D[i][j] - coloana de pe linia M prin care trec ca pe linia i sa ajung pe coloana j */
                
                /* Aici am avut un jeg de bug, considerand ca nu are sens dinamica pentru i < M. Dar daca nu am greutati i > M, eu o lasam necompletata... */
                /* Future me, you should be ashamed of this */
                D[now][j] = j;
                if(i > M) {
                    D[now][j] = D[1 - now][ dq.front() ];
                }

                if(dq.front() + i * cnt[i] == j) dq.pop_front();
            }
        }
    }
    
    int ans = 0;
    for(int i = G; i; i--) 
        if( dp[now][i] != INF ) {
            ans = i;
            break;
        }

    /* daca ma aflu la primul apel al functiei, ala cu toate greutatile disponibile, afisez rezultatul dinamicii. Celelalte apeluri ale functiei au rol doar in reconstruirea solutiei */
    if(L == 1 && R == 200) {
        cout << ans << " " << dp[now][ans] << "\n";
    }

    /* apelez functia mai departe */
    solve(L, M, D[now][ans]);
    solve(M + 1, R, ans - D[now][ans]);

    //cerr << "Exiting " << L << " " << R << " " << G << "\n";
}

int main() {
    citire();
    solve(1, 200, G);
    fclose(stdin); fclose(stdout);
}