#include <iostream> #include <string> #include <cassert> #include <algorithm> #include <vector> #include <set> // Config selector: // 1 == Debug // 0 == Release #if 0 // Debug config #define ASSERT(expr) assert(expr) #define DBG(expr) expr #else // Release config #define ASSERT(expr) do {} while (0) #define DBG(expr) do {} while (0) #endif struct Brick { int length; int pattern; }; struct Tower; struct TowerCompare { // Tower with maximum best_value is ordered first bool operator()(Tower const * a, Tower const * b) const; }; using TowersQueue = std::multiset<Tower const *, TowerCompare>; struct Tower { long long best_value; // valid only if best_value != 0 TowersQueue::iterator towers_queue_it; }; bool TowerCompare::operator()(Tower const * a, Tower const * b) const { return a->best_value > b->best_value; } // It finds the maximum best_value of towers with pattern != brick_pattern (or 0 if no such tower exists). long long find_max_best_value(TowersQueue const & towers_queue, Tower const * towers_vec, int const brick_pattern) { TowersQueue::iterator it = towers_queue.begin(); if (it != towers_queue.end()) { Tower const * tower = *it; int pattern = tower - towers_vec; if (pattern != brick_pattern) return tower->best_value; // try second element ++it; if (it != towers_queue.end()) { tower = *it; pattern = tower - towers_vec; ASSERT(pattern != brick_pattern); return tower->best_value; } } return 0; } int main() { std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); int n; // number of bricks int penalty; // penalty for consecutive bricks with different pattern std::cin >> n >> penalty; ASSERT(n > 0); ASSERT(penalty > 0); // ordered non-incresing, so that we can find solution by iterating from start std::vector<Brick> bricks(n); int max_pattern = 0; for (int i = 0; i < n; ++i) { Brick & brick = bricks[n - 1 - i]; std::cin >> brick.length >> brick.pattern; // let's make pattern 0-based --brick.pattern; max_pattern = std::max(max_pattern, brick.pattern); } // Index is Brick::pattern. An element keeps the best possible tower with top brick pattern given by its index. std::vector<Tower> towers(max_pattern + 1); // only towers with best_value != 0 TowersQueue towers_queue; // Iterate over bricks, each time considering a run of consecutive bricks with the same length. // In each run, we consider each pattern just once. // For each pattern in a run, we generate an update to its best_value. We apply all updates after the whole run. int same_length_start = 0; while (same_length_start < n) { int const brick_length = bricks[same_length_start].length; // find end int same_length_end = same_length_start; do { ++same_length_end; } while (same_length_end < n && bricks[same_length_end].length == brick_length); // sort so that we can skip duplicate patterns std::sort(&bricks[same_length_start], &bricks[same_length_end], [](Brick const & a, Brick const & b) { return a.pattern < b.pattern; }); // update towers, but don't apply updates to best_value yet struct TowerUpdate { int pattern; long long new_best_value; }; std::vector<TowerUpdate> tower_updates; int last_pattern = max_pattern + 1; for (int i = same_length_start; i != same_length_end; ++i) { Brick const & brick = bricks[i]; if (brick.pattern == last_pattern) continue; last_pattern = brick.pattern; int const brick_pattern = brick.pattern; // towers[brick_pattern] new best_value is max of: // * current best_value + brick_length (represents adding the same pattern on top) // * max{best_value for all towers with pattern != brick_pattern} + brick_length - penalty // Make sure to use long long for best_value. TowerUpdate update; update.pattern = brick_pattern; update.new_best_value = std::max(towers[brick_pattern].best_value + brick_length, find_max_best_value(towers_queue, towers.data(), brick_pattern) + brick_length - penalty); tower_updates.push_back(update); } // apply updates to towers' best_value for (TowerUpdate const & update : tower_updates) { // remove first if it exists Tower & tower = towers[update.pattern]; if (tower.best_value) towers_queue.erase(tower.towers_queue_it); // update best_value and insert tower.best_value = update.new_best_value; ASSERT(tower.best_value != 0); tower.towers_queue_it = towers_queue.insert(&tower); } // update start for the next iteration same_length_start = same_length_end; } ASSERT(!towers_queue.empty()); DBG(std::cout << "elements in towers_queue: " << towers_queue.size() << '\n'); DBG(std::cout << "maximum best_value: "); std::cout << (*towers_queue.begin())->best_value << '\n'; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | #include <iostream> #include <string> #include <cassert> #include <algorithm> #include <vector> #include <set> // Config selector: // 1 == Debug // 0 == Release #if 0 // Debug config #define ASSERT(expr) assert(expr) #define DBG(expr) expr #else // Release config #define ASSERT(expr) do {} while (0) #define DBG(expr) do {} while (0) #endif struct Brick { int length; int pattern; }; struct Tower; struct TowerCompare { // Tower with maximum best_value is ordered first bool operator()(Tower const * a, Tower const * b) const; }; using TowersQueue = std::multiset<Tower const *, TowerCompare>; struct Tower { long long best_value; // valid only if best_value != 0 TowersQueue::iterator towers_queue_it; }; bool TowerCompare::operator()(Tower const * a, Tower const * b) const { return a->best_value > b->best_value; } // It finds the maximum best_value of towers with pattern != brick_pattern (or 0 if no such tower exists). long long find_max_best_value(TowersQueue const & towers_queue, Tower const * towers_vec, int const brick_pattern) { TowersQueue::iterator it = towers_queue.begin(); if (it != towers_queue.end()) { Tower const * tower = *it; int pattern = tower - towers_vec; if (pattern != brick_pattern) return tower->best_value; // try second element ++it; if (it != towers_queue.end()) { tower = *it; pattern = tower - towers_vec; ASSERT(pattern != brick_pattern); return tower->best_value; } } return 0; } int main() { std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); int n; // number of bricks int penalty; // penalty for consecutive bricks with different pattern std::cin >> n >> penalty; ASSERT(n > 0); ASSERT(penalty > 0); // ordered non-incresing, so that we can find solution by iterating from start std::vector<Brick> bricks(n); int max_pattern = 0; for (int i = 0; i < n; ++i) { Brick & brick = bricks[n - 1 - i]; std::cin >> brick.length >> brick.pattern; // let's make pattern 0-based --brick.pattern; max_pattern = std::max(max_pattern, brick.pattern); } // Index is Brick::pattern. An element keeps the best possible tower with top brick pattern given by its index. std::vector<Tower> towers(max_pattern + 1); // only towers with best_value != 0 TowersQueue towers_queue; // Iterate over bricks, each time considering a run of consecutive bricks with the same length. // In each run, we consider each pattern just once. // For each pattern in a run, we generate an update to its best_value. We apply all updates after the whole run. int same_length_start = 0; while (same_length_start < n) { int const brick_length = bricks[same_length_start].length; // find end int same_length_end = same_length_start; do { ++same_length_end; } while (same_length_end < n && bricks[same_length_end].length == brick_length); // sort so that we can skip duplicate patterns std::sort(&bricks[same_length_start], &bricks[same_length_end], [](Brick const & a, Brick const & b) { return a.pattern < b.pattern; }); // update towers, but don't apply updates to best_value yet struct TowerUpdate { int pattern; long long new_best_value; }; std::vector<TowerUpdate> tower_updates; int last_pattern = max_pattern + 1; for (int i = same_length_start; i != same_length_end; ++i) { Brick const & brick = bricks[i]; if (brick.pattern == last_pattern) continue; last_pattern = brick.pattern; int const brick_pattern = brick.pattern; // towers[brick_pattern] new best_value is max of: // * current best_value + brick_length (represents adding the same pattern on top) // * max{best_value for all towers with pattern != brick_pattern} + brick_length - penalty // Make sure to use long long for best_value. TowerUpdate update; update.pattern = brick_pattern; update.new_best_value = std::max(towers[brick_pattern].best_value + brick_length, find_max_best_value(towers_queue, towers.data(), brick_pattern) + brick_length - penalty); tower_updates.push_back(update); } // apply updates to towers' best_value for (TowerUpdate const & update : tower_updates) { // remove first if it exists Tower & tower = towers[update.pattern]; if (tower.best_value) towers_queue.erase(tower.towers_queue_it); // update best_value and insert tower.best_value = update.new_best_value; ASSERT(tower.best_value != 0); tower.towers_queue_it = towers_queue.insert(&tower); } // update start for the next iteration same_length_start = same_length_end; } ASSERT(!towers_queue.empty()); DBG(std::cout << "elements in towers_queue: " << towers_queue.size() << '\n'); DBG(std::cout << "maximum best_value: "); std::cout << (*towers_queue.begin())->best_value << '\n'; } |