Cod sursa(job #3259478)

Utilizator andrei.arnautuAndi Arnautu andrei.arnautu Data 26 noiembrie 2024 15:14:58
Problema Coduri Huffman Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 4.14 kb
/**
 *  Worg
 */
#include <deque>
#include <vector>
#include <fstream>
#include <algorithm>

struct Node {
public:
    int left_child_id;
    int right_child_id;
    long long subtree_weight;

    static const long long MAX_WEIGHT = 1e15;
    static const int NULL_ID = -1;

    Node() {}

    Node(const int& _left_child_id, const int& _right_child_id, const long long& _subtree_weight) :
        left_child_id(_left_child_id),
        right_child_id(_right_child_id),
        subtree_weight(_subtree_weight) {
    }
};

class HuffmanCoder {
private:
    int num_symbols;
    int root_id;
    std::vector<Node> nodes;
    std::vector<long long> encodings;
    std::deque<int> symbol_node_ids;
    std::deque<int> inner_node_ids;
    std::vector<char> encoding_lengths;

    void dfs(int node_id, char crt_length, long long crt_value) {
        if (node_id < num_symbols) {
            encodings[node_id] = crt_value;
            encoding_lengths[node_id] = crt_length;
        } else {
            dfs(nodes[node_id].left_child_id, crt_length + 1, crt_value * 2);
            dfs(nodes[node_id].right_child_id, crt_length + 1, crt_value * 2 + 1);
        }
    }

public:
    HuffmanCoder(const int& _num_symbols) : num_symbols(_num_symbols), root_id(Node::NULL_ID) {
        nodes = std::vector<Node>();
        nodes.reserve(num_symbols * 2 + 2);
        encodings = std::vector<long long>(num_symbols);
        encoding_lengths = std::vector<char>(num_symbols);
    }

    void add_symbol(const int symbol_weight) {
        Node symbol_node(Node::NULL_ID, Node::NULL_ID, symbol_weight);
        nodes.push_back(symbol_node);
        symbol_node_ids.push_back((int)nodes.size() - 1);
    }

    int dequeue_min_weight_node() {
        long long min_subtree_weight = Node::MAX_WEIGHT;
        std::deque<int>* deque_ptr = nullptr;

        if (!symbol_node_ids.empty()) {
            min_subtree_weight = nodes[symbol_node_ids.front()].subtree_weight;
            deque_ptr = &symbol_node_ids;
        }

        if (!inner_node_ids.empty() && nodes[inner_node_ids.front()].subtree_weight < min_subtree_weight) {
            min_subtree_weight = nodes[inner_node_ids.front()].subtree_weight;
            deque_ptr = &inner_node_ids;
        }

        int min_weight_node_id = deque_ptr->front();
        deque_ptr->pop_front();
        return min_weight_node_id;
    }

    void build_prefix_code_tree() {
        //  Recursively merge nodes until we only have one left (the root)
        while (symbol_node_ids.size() + inner_node_ids.size() > 1) {
            int left_child_id = dequeue_min_weight_node();
            int right_child_id = dequeue_min_weight_node();
            Node inner_node(left_child_id, right_child_id, nodes[left_child_id].subtree_weight + nodes[right_child_id].subtree_weight);
            nodes.push_back(inner_node);
            inner_node_ids.push_back((int)nodes.size() - 1);

            root_id = (int)nodes.size() - 1;
        }

        if (!symbol_node_ids.empty()) {
            int left_child_id = symbol_node_ids.front();
            nodes.push_back(Node(left_child_id, root_id, nodes[left_child_id].subtree_weight + nodes[root_id].subtree_weight));
            root_id = (int)nodes.size() - 1;
        }
    }

    void build_and_print_encodings() {
        dfs(root_id, 0, 0);

        std::ofstream fout("huffman.out");
        //  Compute the total length
        long long text_encoded_length = 0;
        for (int i = 0; i < num_symbols; i++) {
            text_encoded_length += nodes[i].subtree_weight * encoding_lengths[i];
        }
        fout << text_encoded_length << "\n";
        for (int i = 0; i < num_symbols; i++) {
            fout << (int)encoding_lengths[i] << " " << encodings[i] << '\n';
        }
        fout.close();
    }
};

int main() {
    std::ifstream fin("huffman.in");

    int num_symbols;
    fin >> num_symbols;
    HuffmanCoder huffman_coder(num_symbols);
    for (int symbol_id = 0; symbol_id < num_symbols; symbol_id++) {
        int symbol_weight;
        fin >> symbol_weight;
        huffman_coder.add_symbol(symbol_weight);
    }
    fin.close();

    huffman_coder.build_prefix_code_tree();
    huffman_coder.build_and_print_encodings();
    return 0;
}