├── .gitignore ├── LICENSE ├── README.md ├── algolabVS.sh └── problems ├── week01-build_the_sum ├── README.md └── src │ └── algorithm.cpp ├── week01-dominoes ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week01-even_matrices ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week01-even_pairs ├── README.md └── src │ └── algorithm.cpp ├── week02-beach_bars ├── README.md └── src │ └── algorithm.cpp ├── week02-burning_coins ├── README.md └── src │ └── algorithm.cpp ├── week02-defensive_line ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week02-potw-deck_of_cards ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week02-the_great_game ├── README.md └── src │ └── algorithm.cpp ├── week03-antenna ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week03-first_hit ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week03-hiking_maps ├── README.md └── src │ └── algorithm.cpp ├── week03-hit ├── README.md └── src │ └── algorithm.cpp ├── week03-potw-from_russia_with_love ├── README.md └── src │ └── algorithm.cpp ├── week04-ant_challenge ├── README.md └── src │ └── algorithm.cpp ├── week04-buddy_selection ├── README.md └── src │ └── algorithm.cpp ├── week04-first_steps_bgl ├── README.md └── src │ └── algorithm.cpp ├── week04-important_bridges ├── README.md └── src │ └── algorithm.cpp ├── week04-potw-fighting_pits_mereen ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week05-asterix_the_gaul ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week05-boats ├── README.md └── src │ └── algorithm.cpp ├── week05-moving_books ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week05-potw-motorcycles ├── README.md └── src │ └── algorithm.cpp ├── week05-severus_snape ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week06-diet ├── README.md └── src │ └── algorithm.cpp ├── week06-inball ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week06-lannister ├── README.md └── src │ └── algorithm.cpp ├── week06-maximize_it ├── README.md └── src │ └── algorithm.cpp ├── week06-potw-planet_express ├── README.md └── src │ └── algorithm.cpp ├── week07-coin_tossing ├── README.md └── src │ └── algorithm.cpp ├── week07-knights ├── README.md └── src │ └── algorithm.cpp ├── week07-london ├── README.md └── src │ └── algorithm.cpp ├── week07-potw-octopussy ├── README.md └── src │ └── algorithm.cpp ├── week07-shopping_trip ├── README.md └── src │ └── algorithm.cpp ├── week08-bistro ├── README.md └── src │ └── algorithm.cpp ├── week08-germs ├── README.md └── src │ └── algorithm.cpp ├── week08-h1n1 ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week08-light_the_stage ├── CMakeLists.txt ├── README.md └── src │ ├── algorithm.cpp │ └── algorithm2.cpp ├── week08-potw-suez ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week09-algocoon_group ├── README.md └── src │ └── algorithm.cpp ├── week09-canteen ├── README.md └── src │ └── algorithm.cpp ├── week09-placing_knights ├── README.md └── src │ └── algorithm.cpp ├── week09-potw-kingdom_defence ├── README.md └── src │ └── algorithm.cpp ├── week09-real_estate ├── README.md └── src │ └── algorithm.cpp ├── week10-asterix_and_the_chariot_race ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week10-asterix_in_switzerland ├── README.md └── src │ └── algorithm.cpp ├── week10-evolution ├── README.md └── src │ └── algorithm.cpp ├── week10-potw-goldeneye ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week10-worldcup ├── CMakeLists.txt ├── README.md └── src │ ├── algorithm.cpp │ └── old.cpp ├── week11-asterix_and_the_legions ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week11-idefix ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week11-potw-phantom_menace ├── README.md └── src │ └── algorithm.cpp ├── week11-return_of_the_jedi ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week11-the_iron_islands ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week12-bonus_level ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week12-car_sharing ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week12-dean_thomas ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week12-hong_kong ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week12-majestys_secret_service ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week12-potw-san_francisco ├── README.md └── src │ └── algorithm.cpp ├── week13-hagrid ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week13-hand ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week13-ludo_bagman ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week13-potw-clues ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp ├── week13-punch ├── CMakeLists.txt ├── README.md └── src │ └── algorithm.cpp └── week14-potw-india ├── CMakeLists.txt ├── README.md └── src └── algorithm.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Input/Test Files 35 | public/ 36 | 37 | # Build 38 | build/ 39 | 40 | # Problem descriptions 41 | *.pdf 42 | 43 | # Metadata 44 | *.json 45 | 46 | # Algolab VS 47 | *.algolabVS 48 | 49 | # VS Code 50 | .vscode 51 | 52 | # Debug stuff 53 | debug* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Alex Hägele 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /problems/week01-build_the_sum/README.md: -------------------------------------------------------------------------------- 1 | Dummy problem -------------------------------------------------------------------------------- /problems/week01-build_the_sum/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void testcase() { 4 | int n; std::cin >> n; // Read the number of integers to follow 5 | int result = 0; // Variable storing the result 6 | for (int i = 0; i < n; ++i) { 7 | int a; std::cin >> a; // Read the next number 8 | result += a; // Add it to the result 9 | } 10 | std::cout << result << std::endl; // Output the final result 11 | } 12 | 13 | int main() { 14 | std::ios_base::sync_with_stdio(false); // Always! 15 | int t; std::cin >> t; // Read the number of test cases 16 | for (int i = 0; i < t; ++i) 17 | testcase(); // Solve a particular test case 18 | } -------------------------------------------------------------------------------- /problems/week01-dominoes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week01-dominoes/README.md: -------------------------------------------------------------------------------- 1 | Key ideas: 2 | * linearly go through array, always remember the furthest we can go 3 | * if i+1 <= curr_est_tiles, this dominoe will also be tipped 4 | * then: max(curr_est, i + h[i]) 5 | * end result is min(n, estimate) -------------------------------------------------------------------------------- /problems/week01-dominoes/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void testcase() { 5 | int n; std::cin >> n; // Read the number of integers to follow 6 | std::vector h(n); 7 | for (int i = 0; i < n; ++i) { 8 | std::cin >> h[i]; // Read the next number 9 | } 10 | 11 | int num_tiles = 1; 12 | for (int i = 0; i < n; ++i) { 13 | if((i+1) <= num_tiles) { 14 | num_tiles = std::max(num_tiles, i + h[i]); 15 | } 16 | } 17 | 18 | std::cout << std::min(num_tiles, n) << std::endl; // Output the final result 19 | } 20 | 21 | int main() { 22 | std::ios_base::sync_with_stdio(false); // Always! 23 | int t; std::cin >> t; // Read the number of test cases 24 | for (int i = 0; i < t; ++i) 25 | testcase(); // Solve a particular test case 26 | } -------------------------------------------------------------------------------- /problems/week01-even_matrices/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week01-even_matrices/README.md: -------------------------------------------------------------------------------- 1 | Tags: Prefix Sum 2 | 3 | Same concept as Even Pairs, just 2 dimensions. Overall runtime O(n^3) 4 | 5 | * compute the prefixes in quadratic time 6 | * S[i + 1][j + 1] = S[i + 1][j] + S[i][j + 1] - S[i][j] + v[i][j]; 7 | * For interval (i1,i2,j1,j2): value is S[i2,j2] - S[i2,j1] - S[i1,j2] + S[i1,j1] 8 | * Then, if we fix a range [i1,i2]: 9 | * problem becomes the same as even pairs 10 | * can linearly (for j) compute the expression above and count even and uneven sums -------------------------------------------------------------------------------- /problems/week01-even_matrices/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | typedef std::vector VI; 4 | typedef std::vector VVI; 5 | 6 | 7 | // S[i2,j2] - S[i2,j1] - S[i1,j2] + S[i1,j1] 8 | void testcase() { 9 | int n; std::cin >> n; // Read the number of integers to follow 10 | VVI v(n, VI(n)); 11 | VVI S(n + 1, VI(n + 1)); 12 | for (int i = 0; i < n; ++i) { 13 | for (int j = 0; j < n; ++j) { 14 | int x; std::cin >> x; // Read the next number 15 | v[i][j] = x; 16 | S[i + 1][j + 1] = S[i + 1][j] + S[i][j + 1] - S[i][j] + v[i][j]; 17 | // std::cout << S[i + 1][j + 1] << " "; 18 | } 19 | // std::cout << std::endl; 20 | } 21 | 22 | 23 | int result = 0; 24 | for (int i1 = 1; i1 < n + 1; ++i1) { 25 | for (int i2 = i1; i2 < n + 1; ++i2) { 26 | int even = 0; 27 | int uneven = 0; 28 | for (int j = 1; j < n + 1; ++j) { 29 | int res = S[i2][j] - S[i1-1][j]; 30 | if(res % 2 == 0) { 31 | ++even; 32 | } else { 33 | ++uneven; 34 | } 35 | } 36 | result += even * (even - 1) / 2 + uneven * (uneven - 1) / 2 + even; 37 | } 38 | } 39 | std::cout << result << std::endl; // Output the final result 40 | } 41 | 42 | int main() { 43 | std::ios_base::sync_with_stdio(false); // Always! 44 | int t; std::cin >> t; // Read the number of test cases 45 | for (int i = 0; i < t; ++i) 46 | testcase(); // Solve a particular test case 47 | } 48 | -------------------------------------------------------------------------------- /problems/week01-even_pairs/README.md: -------------------------------------------------------------------------------- 1 | Tags: Prefix Sum 2 | 3 | Classical example of prefix sums, then computing the number of pairs -------------------------------------------------------------------------------- /problems/week01-even_pairs/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void testcase() { 5 | int n; std::cin >> n; // Read the number of integers to follow 6 | std::vector v(n); 7 | int sum = 0; 8 | int num_even = 0; 9 | int num_uneven = 0; 10 | for (int i = 0; i < n; ++i) { 11 | std::cin >> v[i]; // Read the next number 12 | sum += v[i]; 13 | if(sum % 2 == 0) { 14 | ++num_even; 15 | } else { 16 | ++num_uneven; 17 | } 18 | } 19 | int result = num_even * (num_even - 1) / 2; 20 | result += num_uneven * (num_uneven - 1) / 2; 21 | result += num_even; 22 | 23 | std::cout << result << std::endl; // Output the final result 24 | } 25 | 26 | int main() { 27 | std::ios_base::sync_with_stdio(false); // Always! 28 | int t; std::cin >> t; // Read the number of test cases 29 | for (int i = 0; i < t; ++i) 30 | testcase(); // Solve a particular test case 31 | } -------------------------------------------------------------------------------- /problems/week02-beach_bars/README.md: -------------------------------------------------------------------------------- 1 | Tags: Sorting, Sliding Window 2 | 3 | Key ideas: 4 | * First sort positions 5 | * Then, we know that a solution will cover an interval [i,j] -> use a sliding window approach 6 | * i.e., whenever we consider [i,j] we know that if v[j] - v[i] <= 200, we can cover all positions in between 7 | * since multiple solutions are possible, need to keep track of them with a vector or list 8 | * tricky cases: 9 | * if (v[i] + v[j]) % 2 != 0, two solutions possible: 10 | * s1 = v[i] + (v[j] - v[i]) / 2 as well as s1 + 1 11 | * if the window size is the same, compute these possible solution spots to get the distance and take the new solution in case distance is smaller -------------------------------------------------------------------------------- /problems/week02-beach_bars/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | ///2 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | typedef vector VI; 8 | typedef vector VVI; 9 | 10 | void testcase() { 11 | int n; cin >> n; // Read the number of integers to follow 12 | VI v(n); 13 | for(int i = 0; i < n; i++) { 14 | cin >> v[i]; 15 | } 16 | sort(v.begin(), v.end()); 17 | int i = 0; 18 | int j = 0; 19 | int max_num_par = 0; 20 | int min_long_dist = 101; 21 | vector positions; 22 | 23 | while (j < n) { 24 | // cout << "i " << i << " j " << j << endl; 25 | if(v[j] - v[i] <= 200) { // parasols betw. i and j can be covered 26 | if(j - i + 1 > max_num_par) { // new best 27 | // cout << "here" << endl; 28 | positions.clear(); 29 | max_num_par = j - i + 1; 30 | int pos = v[i] + (v[j] - v[i]) / 2; 31 | positions.push_back(pos); 32 | if((v[i] + v[j]) % 2 == 0) { 33 | min_long_dist = pos - v[i]; 34 | } else { 35 | positions.push_back(pos + 1); 36 | min_long_dist = pos + 1 - v[i]; 37 | } 38 | } else if(j - i + 1 == max_num_par) { // same num. parasols 39 | int pos = v[i] + (v[j] - v[i]) / 2; 40 | int this_long_dist = (v[i] + v[j]) % 2 == 0 ? pos - v[i] : pos - v[i] + 1; 41 | if(this_long_dist <= min_long_dist) { 42 | if(this_long_dist < min_long_dist) { 43 | positions.clear(); 44 | min_long_dist = this_long_dist; 45 | } 46 | positions.push_back(pos); 47 | if((v[i] + v[j]) % 2 != 0) { 48 | positions.push_back(pos + 1); 49 | } 50 | } 51 | } 52 | j++; // grow sliding window 53 | } else { 54 | i++; 55 | if (i > j) { 56 | // if (i == n) break; 57 | j = i; 58 | } 59 | } 60 | } 61 | cout << max_num_par << " " << min_long_dist << endl; 62 | for(int i : positions) { 63 | cout << i << " "; 64 | } 65 | cout << endl; 66 | } 67 | 68 | int main() { 69 | ios_base::sync_with_stdio(false); // Always! 70 | int t; cin >> t; // Read the number of test cases 71 | for (int i = 0; i < t; ++i) 72 | testcase(); // Solve a particular test case 73 | } 74 | -------------------------------------------------------------------------------- /problems/week02-burning_coins/README.md: -------------------------------------------------------------------------------- 1 | Tags: Dynamic Programming 2 | 3 | Key ideas: 4 | * Classical DP: either take left or right, but the recursion repeats itself for [i,j] coins left 5 | * i.e., DP table of size n * n 6 | * in order to consider "guaranteed" amount, asssume other player is _minimizing_ your return 7 | * Denote the possibilities l_r == you take left, other player takes right, and accordingly for the other options. 8 | * recursion is thus: max(min(l_r, l_l) + v[i], min(r_r, r_l) + v[j]); -------------------------------------------------------------------------------- /problems/week02-burning_coins/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef std::vector VI; 5 | typedef std::vector VVI; 6 | 7 | int largest_amount(VVI &m, VI &v, int i, int j) { 8 | if(i == j) { // one coin left 9 | return v[i]; 10 | } else if (i > j) { // we took the last coin 11 | return 0; 12 | } else if(m[i][j] != -1) { // already computed 13 | return m[i][j]; 14 | } 15 | int l_r = largest_amount(m, v, i + 1, j - 1); 16 | int l_l = largest_amount(m, v, i + 2, j); 17 | int r_l = largest_amount(m, v, i + 1, j - 1); 18 | int r_r = largest_amount(m, v, i, j - 2); 19 | int min_left = std::min(l_r, l_l); 20 | int min_right = std::min(r_l, r_r); 21 | int best = std::max(min_left + v[i], min_right + v[j]); 22 | m[i][j] = best; 23 | return best; 24 | } 25 | 26 | void testcase() { 27 | int n; std::cin >> n; 28 | VI v(n); 29 | VVI m(n, VI(n, -1)); 30 | for(int i = 0; i < n; i++) { 31 | std::cin >> v[i]; 32 | } 33 | std::cout << largest_amount(m, v, 0, n - 1) << std::endl; 34 | } 35 | 36 | 37 | int main() { 38 | 39 | std::ios_base::sync_with_stdio(false); // Always! 40 | int t; std::cin >> t; 41 | for(int i = 0; i < t; i++) { 42 | testcase(); 43 | } 44 | } -------------------------------------------------------------------------------- /problems/week02-defensive_line/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week02-defensive_line/README.md: -------------------------------------------------------------------------------- 1 | Tags: Sliding Window, Dynamic Programming 2 | 3 | Key ideas: 4 | * Sliding window to compute: 5 | * length array of size n: length of window ending in defender i that has value exactly k 6 | * -1 if there is no window 7 | * then iterative DP (recursive timed out for me): 8 | * table of size m * n 9 | * dp[i][j] : max number of defenders between [0,j] that are attacked 10 | * and _all_ attackers up to i are used -------------------------------------------------------------------------------- /problems/week02-defensive_line/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int dp[101][100'001]; 5 | int len[100'001]; 6 | 7 | int solve(std::vector> &dp, size_t att_i, size_t def_j) { 8 | // std::cerr << att_i << " " << def_j << "\n"; 9 | if(att_i == 0) { 10 | return 0; 11 | } 12 | if(def_j == 0) { 13 | return -1; 14 | } 15 | 16 | if(dp[att_i][def_j] != -1) { 17 | return dp[att_i][def_j]; 18 | } 19 | 20 | 21 | int len_at = len[def_j - 1]; 22 | if(len_at != -1) { 23 | int prev_res = solve(dp, att_i - 1, def_j - len_at); 24 | if(prev_res != -1) { 25 | dp[att_i][def_j] = 26 | std::max(prev_res + len_at, solve(dp, att_i, def_j - 1)); 27 | return dp[att_i][def_j]; 28 | } 29 | } 30 | 31 | dp[att_i][def_j] = solve(dp, att_i, def_j - 1); 32 | return dp[att_i][def_j]; 33 | } 34 | 35 | void testcase() { 36 | int n, m, k; 37 | std::cin >> n >> m >> k; 38 | 39 | std::vector v(n); 40 | // std::vector> dp(m + 1, std::vector(n + 1, -1)); 41 | 42 | for(int i = 0; i < n; i++) { 43 | std::cin >> v[i]; 44 | // len: length of window ending in i that has value exactly k, 45 | // -1 if there is no window 46 | len[i] = -1; 47 | } 48 | 49 | int l = 0; 50 | int r = 0; 51 | int sum = v[l]; 52 | while(l < n) { 53 | if(sum == k) { 54 | len[r] = r - l + 1; 55 | sum -= v[l]; 56 | ++l; 57 | if(l == n) break; 58 | } else if(sum < k) { 59 | ++r; 60 | if(r == n) { 61 | break; 62 | } 63 | sum += v[r]; 64 | } else { 65 | sum -= v[l]; 66 | ++l; 67 | if(l > r) { 68 | if(l == n) break; 69 | sum = v[l]; 70 | r = l; 71 | } 72 | } 73 | } 74 | 75 | for(int i = 0; i <= n; i++) { 76 | dp[0][i] = 0; 77 | // if(i < n) std::cerr << len[i] << " "; 78 | } 79 | for(int i = 1; i <= m; i++) { 80 | dp[i][0] = -1; 81 | } 82 | 83 | // dp[i][j] : max number of defenders between [0,j] that are attacked 84 | // and _all_ attackers up to i are used 85 | for(int i = 1; i <= m; i++) { 86 | for(int j = 1; j <= n; j++) { 87 | const int len_at = len[j-1]; 88 | if (len_at != -1 && dp[i-1][j-len_at] != -1) { 89 | dp[i][j] = std::max(dp[i-1][j-len_at] + len_at, dp[i][j-1]); 90 | } else { 91 | dp[i][j] = dp[i][j-1]; 92 | } 93 | } 94 | } 95 | 96 | // std::cerr << "\n"; 97 | const int res = dp[m][n]; 98 | 99 | // timelimit for the recursive one 100 | // int res = solve(dp, m, n); 101 | if(res != -1) { 102 | std::cout << res << "\n"; 103 | return; 104 | } 105 | std::cout << "fail" << std::endl; 106 | } 107 | 108 | int main() 109 | { 110 | std::ios_base::sync_with_stdio(false); 111 | std::size_t t; 112 | for (std::cin >> t; t > 0; --t) testcase(); 113 | return 0; 114 | } -------------------------------------------------------------------------------- /problems/week02-potw-deck_of_cards/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week02-potw-deck_of_cards/README.md: -------------------------------------------------------------------------------- 1 | Tags: Prefix Sum, Sliding Window 2 | 3 | Key ideas: 4 | * Typical sliding window exercise, which just needs precomputing the prefix sums (v up to i) 5 | * lexicographical order is fulfilled by only updating value when strictly better -------------------------------------------------------------------------------- /problems/week02-potw-deck_of_cards/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | /// 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | void testcase() { 8 | int n; std::cin >> n; 9 | int k; std::cin >> k; 10 | std::vector v(n); 11 | std::vector S(n+1); 12 | int sum = 0; 13 | int best_i = 0; 14 | int best_j = 0; 15 | 16 | for(int i = 0; i < n; i++) { 17 | std::cin >> v[i]; 18 | S[i] = sum; 19 | sum += v[i]; 20 | } 21 | S[n] = sum; 22 | 23 | /////////////////////////////// SLIDING WINDOW 24 | int best_diff = abs(v[0] - k); 25 | int l = 0; 26 | int r = 0; 27 | int curr_sum = v[0]; 28 | while(l < n && r < n) { 29 | if(curr_sum == k) { 30 | break; 31 | } else if(curr_sum < k) { 32 | r++; 33 | } else { 34 | if(l == r) { 35 | r++; 36 | } else { 37 | l++; 38 | } 39 | } 40 | curr_sum = S[r + 1] - S[l]; 41 | int diff = abs(k - curr_sum); 42 | 43 | if(diff < best_diff) { 44 | // std::cout << "i " << i << " j " << j << " diff " << diff << std::endl; 45 | best_i = l; 46 | best_j = r; 47 | best_diff = diff; 48 | } 49 | } 50 | std::cout << best_i << " " << best_j << std::endl; // Output the final result 51 | } 52 | 53 | int main() { 54 | std::ios_base::sync_with_stdio(false); // Always! 55 | int t; std::cin >> t; // Read the number of test cases 56 | for (int i = 0; i < t; ++i) 57 | testcase(); // Solve a particular test case 58 | } -------------------------------------------------------------------------------- /problems/week02-the_great_game/README.md: -------------------------------------------------------------------------------- 1 | Tags: Dynamic Programming 2 | 3 | Key ideas: 4 | * two arrays denoting the number of moves needed to get to position n 5 | * one if we want to maximize number of moves, one for minimizing 6 | * players alternate and want to minimize their own moves while maximizing for the other 7 | * hence for transitions i->j: 8 | * min_moves[i] = std::min(1 + max_moves[j]); 9 | * max_moves[i] = std::max(1 + min_moves[j]); 10 | * sherlock wins if (* r and b positions of figures): 11 | * min_moves[r - 1] < min_moves[b - 1] or 12 | * (min_moves[r - 1] == min_moves[b - 1] && min_moves[r - 1] % 2 == 1) (since he starts) -------------------------------------------------------------------------------- /problems/week02-the_great_game/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | ///3 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void testcase() { 9 | size_t n, m, r, b; 10 | std::cin >> n >> m >> r >> b; 11 | 12 | std::vector min_moves(n, -1), max_moves(n, -1); 13 | 14 | min_moves[n - 1] = 0; 15 | max_moves[n - 1] = 0; 16 | 17 | std::vector> transition(n); 18 | for(size_t i = 0; i < m; i++) { 19 | size_t u, v; 20 | std::cin >> u >> v; 21 | transition[u-1].push_back(v-1); 22 | } 23 | 24 | for(int i = n - 2; i >= 0; --i) { 25 | min_moves[i] = std::numeric_limits::max(); 26 | max_moves[i] = std::numeric_limits::min(); 27 | for(size_t j : transition[i]) { 28 | min_moves[i] = std::min(min_moves[i], 1 + max_moves[j]); 29 | max_moves[i] = std::max(max_moves[i], 1 + min_moves[j]); 30 | } 31 | } 32 | 33 | if(min_moves[r - 1] < min_moves[b - 1] || 34 | (min_moves[r - 1] == min_moves[b - 1] && min_moves[r - 1] % 2 == 1)) { 35 | std::cout << 0 << std::endl; 36 | } else { 37 | std::cout << 1 << std::endl; 38 | } 39 | } 40 | 41 | int main() 42 | { 43 | std::ios_base::sync_with_stdio(false); 44 | std::size_t t; 45 | for (std::cin >> t; t > 0; --t) testcase(); 46 | return 0; 47 | } -------------------------------------------------------------------------------- /problems/week03-antenna/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week03-antenna/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week03-antenna/README.md -------------------------------------------------------------------------------- /problems/week03-antenna/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | typedef CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt K; 5 | typedef K::Point_2 P; 6 | typedef CGAL::Min_circle_2_traits_2 Traits; 7 | typedef CGAL::Min_circle_2 Min_circle; 8 | 9 | using namespace std; 10 | 11 | double ceil_to_double(const K::FT& x) 12 | { 13 | double a = std::ceil((CGAL::to_double(x))); 14 | while (a-1 >= x) a -= 1; 15 | while (a < x) a += 1; 16 | return a; 17 | } 18 | 19 | int main() 20 | { 21 | ios_base::sync_with_stdio(false); 22 | int n; std::cin >> n; 23 | while(n > 0) { 24 | vector

points(n); 25 | for(int i = 0; i < n; i++) { 26 | long x, y; 27 | cin >> x; cin >> y; 28 | P p = P(x,y); 29 | points[i] = p; 30 | } 31 | // random_shuffle(points.begin(), points.end()); 32 | Min_circle mc(points.begin(), points.end(), true); 33 | Traits::Circle c = mc.circle(); 34 | cout << long(ceil_to_double(CGAL::sqrt(c.squared_radius()))) << endl; 35 | cin >> n; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /problems/week03-first_hit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week03-first_hit/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week03-first_hit/README.md -------------------------------------------------------------------------------- /problems/week03-first_hit/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | //1 2 | #include 3 | typedef CGAL::Exact_predicates_exact_constructions_kernel K; 4 | typedef K::Point_2 P; 5 | typedef K::Segment_2 S; 6 | typedef K::Ray_2 R; 7 | using namespace std; 8 | 9 | double floor_to_double(const K::FT& x) 10 | { 11 | double a = std::floor(CGAL::to_double(x)); 12 | while (a > x) a -= 1; 13 | while (a+1 <= x) a += 1; 14 | return a; 15 | } 16 | 17 | int main() 18 | { 19 | ios_base::sync_with_stdio(false); 20 | int n; std::cin >> n; 21 | while(n > 0) { 22 | long x, y, a, b; 23 | cin >> x; cin >> y; cin >> a; cin >> b; 24 | P source = P(x,y); 25 | P closest_hit; 26 | R ray(source, P(a,b)); 27 | S hit_seg; 28 | bool hits = false; 29 | vector segments(n); 30 | 31 | for(int i = 0; i < n; i++) { 32 | long r, s, t, u; 33 | cin >> r; cin >> s; cin >> t; cin >> u; 34 | P seg_p1 = P(r,s); 35 | P seg_p2 = P(t,u); 36 | S seg(seg_p1, seg_p2); 37 | segments[i] = seg; 38 | } 39 | random_shuffle(segments.begin(), segments.end()); 40 | 41 | for(int i = 0; i < n; i++) { 42 | S seg = segments[i]; 43 | P seg_p1 = seg.source(); 44 | P seg_p2 = seg.target(); 45 | if(!hits && CGAL::do_intersect(ray,seg)) { // no hit yet, i.e. only have ray 46 | auto o = CGAL::intersection(ray,seg); 47 | if (const P* op = boost::get

(&*o)) { // point intersec 48 | closest_hit = *op; 49 | } else { // segment intersec 50 | closest_hit = CGAL::has_larger_distance_to_point(source, seg_p1, seg_p2) ? seg_p2 : seg_p1; 51 | } 52 | hit_seg = S(source, closest_hit); 53 | hits = true; 54 | } else if(CGAL::do_intersect(hit_seg,seg)) { // hit once, so we have a segment 55 | auto o = CGAL::intersection(hit_seg,seg); 56 | if (const P* op = boost::get

(&*o)) { // point intersec 57 | closest_hit = *op; 58 | hit_seg = S(source, closest_hit); 59 | } else { // segment intersec 60 | closest_hit = CGAL::has_larger_distance_to_point(source, seg_p1, seg_p2) ? seg_p2 : seg_p1; 61 | hit_seg = S(source, closest_hit); 62 | } 63 | } 64 | } 65 | if(!hits) 66 | cout << "no" << endl; 67 | else 68 | cout << long(floor_to_double(closest_hit.x())) << " " << long(floor_to_double(closest_hit.y())) << endl; 69 | cin >> n; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /problems/week03-hiking_maps/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week03-hiking_maps/README.md -------------------------------------------------------------------------------- /problems/week03-hiking_maps/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | ///1 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | typedef CGAL::Exact_predicates_exact_constructions_kernel K; 9 | typedef K::Point_2 P; 10 | typedef K::Line_2 L; 11 | // typedef std::tuple leg; 12 | 13 | using namespace std; 14 | 15 | 16 | void testcase() { 17 | int m, n; 18 | cin >> m; cin >> n; 19 | vector

leg_points(m); 20 | for(int i = 0; i < m; i++) { 21 | int x, y; cin >> x; cin >> y; 22 | leg_points[i] = P(x, y); 23 | } 24 | 25 | vector> triang_covers_leg(n, vector(0)); 26 | 27 | for(int i = 0; i < n; i++) { 28 | vector

q(6); 29 | for(int j = 0; j < 6; j++) { 30 | int x, y; cin >> x; cin >> y; 31 | q[j] = P(x,y); 32 | } 33 | L e0 = L(q[0], q[1]); 34 | L e1 = L(q[2], q[3]); 35 | L e2 = L(q[4], q[5]); 36 | 37 | auto o0 = CGAL::intersection(e0,e1); 38 | const P* p0 = boost::get

(&*o0); // point intersec guaranteed 39 | auto o1 = CGAL::intersection(e0,e2); 40 | const P* p1 = boost::get

(&*o1); // point intersec guaranteed 41 | auto o2 = CGAL::intersection(e1,e2); 42 | const P* p2 = boost::get

(&*o2); // point intersec guaranteed 43 | vector covers_leg(m, false); 44 | for(int j = 0; j < m; j++) { // check for every point if it's covered 45 | P s = leg_points[j]; 46 | auto or0 = CGAL::orientation(*p0, *p1, s); 47 | auto or1 = CGAL::orientation(*p1, *p2, s); 48 | auto or2 = CGAL::orientation(*p2, *p0, s); 49 | covers_leg[j] = (or0 <= 0 && or1 <= 0 && or2 <= 0) 50 | || (or0 >= 0 && or1 >= 0 && or2 >= 0); 51 | } 52 | for(int j = 0; j < m - 1; j++) { // legs only covered if both j and j+1 covered 53 | if(covers_leg[j] && covers_leg[j+1]) triang_covers_leg[i].push_back(j); 54 | } 55 | } 56 | vector is_covered(m - 1, 0); 57 | bool all_covered; 58 | int min_k = n; 59 | int e = 1, b = 0; 60 | for(int k : triang_covers_leg[b]) { 61 | is_covered[k]++; 62 | } 63 | 64 | while(true) { 65 | all_covered = true; 66 | for(int j = 0; j < m - 1; j++) { 67 | all_covered = all_covered && (is_covered[j] > 0); 68 | } 69 | if(all_covered) { 70 | min_k = min(min_k, e - b); 71 | for(int k : triang_covers_leg[b]) { 72 | is_covered[k]--; // do not cover legs of b anymore 73 | } 74 | b++; 75 | if(b == n) break; 76 | } else { 77 | e++; 78 | if(e == n + 1) break; 79 | for(int k : triang_covers_leg[e - 1]) { 80 | is_covered[k]++; // now cover additional legs by e-1 81 | } 82 | } 83 | if(e == b) { 84 | e++; 85 | for(int k : triang_covers_leg[e - 1]) { 86 | is_covered[k]++; // do not cover legs of b anymore 87 | } 88 | } 89 | } 90 | 91 | cout << min_k << endl; 92 | } 93 | int main() 94 | { 95 | ios_base::sync_with_stdio(false); 96 | int c; std::cin >> c; 97 | for(int i = 0; i < c; ++i) { 98 | testcase(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /problems/week03-hit/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week03-hit/README.md -------------------------------------------------------------------------------- /problems/week03-hit/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | typedef CGAL::Exact_predicates_exact_constructions_kernel K; 3 | typedef K::Point_2 P; 4 | typedef K::Segment_2 S; 5 | typedef K::Ray_2 R; 6 | using namespace std; 7 | 8 | int main() 9 | { 10 | std::ios_base::sync_with_stdio(false); 11 | int n; std::cin >> n; 12 | while(n > 0) { 13 | long x, y, a, b; 14 | cin >> x; cin >> y; cin >> a; cin >> b; 15 | R ray(P(x,y), P(a,b)); 16 | bool hits = false; 17 | for(int i = 0; i < n; i++) { 18 | long r, s, t, u; 19 | cin >> r; cin >> s; cin >> t; cin >> u; 20 | if(!hits) { 21 | S seg(P(r,s), P(t,u)); 22 | hits = CGAL::do_intersect(ray,seg) ? true : hits; 23 | } 24 | } 25 | string res = hits ? "yes" : "no"; 26 | cout << res << endl; 27 | cin >> n; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /problems/week03-potw-from_russia_with_love/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week03-potw-from_russia_with_love/README.md -------------------------------------------------------------------------------- /problems/week03-potw-from_russia_with_love/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | ///2 2 | 3 | #include 4 | #include 5 | #include 6 | typedef std::vector VI; 7 | typedef std::vector VVI; 8 | 9 | int largest_amount(int k, int m, VVI &memo, VI &v, int i, int j) { 10 | if(i == j) { // one coin left 11 | return v[i]; 12 | } else if (i > j) { // we took the last coin 13 | return 0; 14 | } else if(memo[i][j] != -1) { // already computed 15 | return memo[i][j]; 16 | } 17 | int min_left = INT_MAX; 18 | for(int l = 0; l < m; l++) { 19 | min_left = std::min(min_left, largest_amount(k, m, memo, v, i + 1 + l, j - m + l + 1)); 20 | } 21 | min_left += v[i]; 22 | 23 | int min_right = INT_MAX; 24 | for(int l = 0; l < m; l++) { 25 | min_right = std::min(min_right, largest_amount(k, m, memo, v, i + l, j - m + l)); 26 | } 27 | min_right += v[j]; 28 | 29 | int best = std::max(min_left, min_right); 30 | memo[i][j] = best; 31 | return best; 32 | } 33 | 34 | void testcase() { 35 | int n; std::cin >> n; 36 | int m; std::cin >> m; 37 | int k; std::cin >> k; 38 | VI v(n); 39 | VVI memo(n, VI(n, -1)); 40 | for(int i = 0; i < n; i++) { 41 | std::cin >> v[i]; 42 | } 43 | int best = INT_MAX; 44 | for(int i = 0; i <= k; i++) { 45 | best = std::min(best, largest_amount(k, m, memo, v, i, n - k - 1 + i)); 46 | } 47 | std::cout << best << std::endl; 48 | } 49 | 50 | 51 | int main() { 52 | 53 | std::ios_base::sync_with_stdio(false); // Always! 54 | int t; std::cin >> t; 55 | for(int i = 0; i < t; i++) { 56 | testcase(); 57 | } 58 | } -------------------------------------------------------------------------------- /problems/week04-ant_challenge/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week04-ant_challenge/README.md -------------------------------------------------------------------------------- /problems/week04-ant_challenge/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // STL includes 2 | #include 3 | #include 4 | 5 | // BGL includes 6 | #include 7 | #include 8 | #include 9 | 10 | typedef boost::adjacency_list > weighted_graph; 12 | typedef boost::property_map::type weight_map; 13 | typedef boost::graph_traits::edge_descriptor edge_desc; 14 | typedef boost::graph_traits::vertex_descriptor vertex_desc; 15 | 16 | using namespace std; 17 | 18 | int dijkstra(const weighted_graph &G, int s, int t) { 19 | int n = boost::num_vertices(G); 20 | std::vector dist_map(n); 21 | 22 | boost::dijkstra_shortest_paths(G, s, 23 | boost::distance_map(boost::make_iterator_property_map( 24 | dist_map.begin(), boost::get(boost::vertex_index, G)))); 25 | 26 | return dist_map[t]; 27 | } 28 | 29 | void kruskal(const weighted_graph &G, vector &mst) { 30 | boost::kruskal_minimum_spanning_tree(G, std::back_inserter(mst)); 31 | } 32 | 33 | 34 | void testcase() { 35 | int n; cin >> n; 36 | int m; cin >> m; 37 | int s; cin >> s; 38 | int a, b; cin >> a; cin >> b; 39 | vector Gs(s, weighted_graph(n)); 40 | weighted_graph G(n); 41 | vector weights(s); 42 | 43 | for(int i = 0; i < s; i++) 44 | weights[i] = boost::get(boost::edge_weight, Gs[i]); 45 | 46 | weight_map weights_g = boost::get(boost::edge_weight, G); 47 | edge_desc e; 48 | for(int i = 0; i < m; i++) { 49 | int u,v; cin >> u; cin >> v; 50 | for(int j = 0; j < s; j++) { 51 | int w; cin >> w; 52 | e = boost::add_edge(u, v, Gs[j]).first; weights[j][e] = w; 53 | } 54 | } 55 | 56 | int hive; // disregard hives 57 | for(int i = 0; i < s; i++) { 58 | cin >> hive; 59 | } 60 | for(int i = 0; i < s; i++) { 61 | vector mst; 62 | kruskal(Gs[i], mst); 63 | for (std::vector::iterator it = mst.begin(); it != mst.end(); ++it) { 64 | int u = boost::source(*it, Gs[i]); 65 | int v = boost::target(*it, Gs[i]); 66 | if(!edge(u,v,G).second) { // edge not yet in G 67 | e = boost::add_edge(u, v, G).first; 68 | weights_g[e] = weights[i][*it]; 69 | } else { 70 | e = edge(u,v,G).first; 71 | weights_g[e] = min(weights_g[e], weights[i][*it]); 72 | } 73 | } 74 | } 75 | 76 | cout << dijkstra(G, a, b) << endl; 77 | 78 | } 79 | int main() 80 | { 81 | std::ios_base::sync_with_stdio(false); // Always! 82 | int t; cin >> t; 83 | for(int i = 0; i < t; i++) 84 | testcase(); 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /problems/week04-buddy_selection/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week04-buddy_selection/README.md -------------------------------------------------------------------------------- /problems/week04-buddy_selection/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // STL includes 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // BGL includes 8 | #include 9 | #include 10 | #include 11 | 12 | typedef boost::adjacency_list graph; 13 | typedef boost::graph_traits::vertex_descriptor vertex_desc; 14 | 15 | using namespace std; 16 | 17 | 18 | int maximum_matching(const graph &G) { 19 | int n = boost::num_vertices(G); 20 | std::vector mate_map(n); // exterior property map 21 | // const vertex_desc NULL_VERTEX = boost::graph_traits::null_vertex(); 22 | 23 | boost::edmonds_maximum_cardinality_matching(G, 24 | boost::make_iterator_property_map(mate_map.begin(), boost::get(boost::vertex_index, G))); 25 | int matching_size = boost::matching_size(G, 26 | boost::make_iterator_property_map(mate_map.begin(), boost::get(boost::vertex_index, G))); 27 | return matching_size; 28 | // int min_weight = INT_MAX; 29 | // edge_desc e; 30 | // for (int i = 0; i < n; ++i) { 31 | // // mate_map[i] != NULL_VERTEX: the vertex is matched 32 | // // i < mate_map[i]: visit each edge in the matching only once 33 | // // cout << i << " " << mate_map[i] << endl; 34 | // e = edge(i,mate_map[i],G).first; 35 | // min_weight = min(min_weight, weights[e]); 36 | // } 37 | // return min_weight; 38 | } 39 | 40 | void testcase() 41 | { 42 | int n; cin >> n; 43 | int c; cin >> c; 44 | int f; cin >> f; 45 | graph G(n); 46 | vector> characteristics(n); 47 | for(int i = 0; i < n; i++) { 48 | characteristics[i] = unordered_set(0); 49 | for(int k = 0; k < c; k++) { 50 | string s; cin >> s; 51 | characteristics[i].insert(s); 52 | } 53 | for(int j = 0; j < i; j++) { 54 | int common = 0; 55 | for(auto s : characteristics[j]) { 56 | if(characteristics[i].count(s) > 0) common++; 57 | } 58 | // cout << i << " " << j << " " << common << " "; 59 | if(common > f) { 60 | // cout << i << " " << j << endl; 61 | boost::add_edge(i, j, G); 62 | } 63 | // cout << e << " " << weights[e] << endl; 64 | } 65 | } 66 | 67 | int matching_size = maximum_matching(G); 68 | // std::cout << matching_size << endl; 69 | if(matching_size == n / 2) { 70 | cout << "not optimal" << endl; 71 | } else { 72 | cout << "optimal" << endl; 73 | } 74 | } 75 | 76 | int main() { 77 | std::ios_base::sync_with_stdio(false); // Always! 78 | int t; cin >> t; 79 | while(t--) testcase(); 80 | } 81 | -------------------------------------------------------------------------------- /problems/week04-first_steps_bgl/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week04-first_steps_bgl/README.md -------------------------------------------------------------------------------- /problems/week04-first_steps_bgl/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // STL includes 2 | #include 3 | #include 4 | 5 | // BGL includes 6 | #include 7 | #include 8 | #include 9 | 10 | typedef boost::adjacency_list > weighted_graph; 12 | typedef boost::property_map::type weight_map; 13 | typedef boost::graph_traits::edge_descriptor edge_desc; 14 | typedef boost::graph_traits::vertex_descriptor vertex_desc; 15 | 16 | using namespace std; 17 | 18 | int furthest_dist(const weighted_graph &G, int s) { 19 | int n = boost::num_vertices(G); 20 | std::vector dist_map(n); 21 | 22 | boost::dijkstra_shortest_paths(G, s, 23 | boost::distance_map(boost::make_iterator_property_map( 24 | dist_map.begin(), boost::get(boost::vertex_index, G)))); 25 | 26 | int max_dist = 0; 27 | for(int i = 1; i < n; i++) { 28 | if(dist_map[i] > max_dist) 29 | max_dist = dist_map[i]; 30 | } 31 | return max_dist; 32 | } 33 | 34 | int span_tree_weight(const weighted_graph &G, const weight_map &weights) { 35 | std::vector mst; // vector to store MST edges (not a property map!) 36 | 37 | boost::kruskal_minimum_spanning_tree(G, std::back_inserter(mst)); 38 | int total_weight = 0; 39 | for (std::vector::iterator it = mst.begin(); it != mst.end(); ++it) { 40 | total_weight += weights[*it]; 41 | } 42 | return total_weight; 43 | } 44 | 45 | 46 | void testcase() { 47 | int n; cin >> n; 48 | int m; cin >> m; 49 | weighted_graph G(n); 50 | weight_map weights = boost::get(boost::edge_weight, G); 51 | edge_desc e; 52 | for(int i = 0; i < m; i++) { 53 | int u,v; cin >> u; cin >> v; 54 | int w; cin >> w; 55 | e = boost::add_edge(u, v, G).first; weights[e] = w; 56 | } 57 | 58 | cout << span_tree_weight(G, weights) << " "; 59 | cout << furthest_dist(G, 0) << endl; 60 | 61 | } 62 | int main() 63 | { 64 | std::ios_base::sync_with_stdio(false); // Always! 65 | int t; cin >> t; 66 | for(int i = 0; i < t; i++) 67 | testcase(); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /problems/week04-important_bridges/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week04-important_bridges/README.md -------------------------------------------------------------------------------- /problems/week04-important_bridges/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // STL includes 2 | #include 3 | #include 4 | 5 | // BGL includes 6 | #include 7 | #include 8 | 9 | 10 | using namespace std; 11 | 12 | struct topological { 13 | bool operator() (const pair t0, const pair t1) { 14 | return (t0.first < t1.first) || (t0.first == t1.first && t0.second < t1.second); 15 | } 16 | }; 17 | 18 | typedef boost::adjacency_list > graph; 20 | typedef boost::property_map::type edge_map; 21 | typedef boost::graph_traits::edge_descriptor edge_desc; 22 | typedef boost::graph_traits::vertex_descriptor vertex_desc; 23 | typedef boost::graph_traits::edge_iterator edge_it; 24 | 25 | void important_bridges(graph &G) { 26 | edge_map component = boost::get(boost::edge_weight, G); 27 | int ncc = boost::biconnected_components(G, component); 28 | 29 | vector> imp_bridges(0); 30 | edge_it ebeg, eend; 31 | vector size_ncc(ncc, 0); 32 | for (boost::tie(ebeg, eend) = boost::edges(G); ebeg != eend; ++ebeg) { 33 | size_ncc[component[*ebeg]]++; 34 | } 35 | 36 | for (boost::tie(ebeg, eend) = boost::edges(G); ebeg != eend; ++ebeg) { 37 | int u = boost::source(*ebeg, G), v = boost::target(*ebeg, G); 38 | if (size_ncc[component[*ebeg]] == 1) imp_bridges.push_back({min(u,v),max(u,v)}); 39 | } 40 | std::sort(imp_bridges.begin(), imp_bridges.end(), topological()); 41 | cout << imp_bridges.size() << endl; 42 | for(auto e : imp_bridges) 43 | cout << e.first << " " << e.second << endl; 44 | 45 | } 46 | 47 | void testcase() 48 | { 49 | int n; cin >> n; 50 | int m; cin >> m; 51 | graph G(n); 52 | for(int i = 0; i < m; i++) { 53 | int e1, e2; cin >> e1; cin >> e2; 54 | boost::add_edge(e1, e2, G); 55 | } 56 | 57 | important_bridges(G); 58 | } 59 | 60 | int main() { 61 | std::ios_base::sync_with_stdio(false); // Always! 62 | int t; cin >> t; 63 | while(t--) testcase(); 64 | } 65 | -------------------------------------------------------------------------------- /problems/week04-potw-fighting_pits_mereen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week04-potw-fighting_pits_mereen/README.md: -------------------------------------------------------------------------------- 1 | Tags: Dynamic Programming 2 | 3 | Key ideas: 4 | --- 5 | * Ugly DP task, with a very complicated DP relation as well as computations... 6 | * essentially, when we define the recursive relation, it depends on 7 | 1. the current fighter next in the queue 8 | 2. the previous fighters north as well as south 9 | 3. the difference in the queues (p-q) (note that the difference is enough, bc. we always just increase and/or decrease it) 10 | * Then, we can decide if we put the next one north or south and, as usual in DP, take the better option depending on the recursive solutions 11 | * The lookup depends on: n, q_north, q_south, diff 12 | * need to transform them into indices! 13 | 14 | Important observations to have compute the indices as well as shrinking the DP table: 15 | 1. For the queues: 16 | * we only need to consider the last two fighters to compute the index - if we consider a single recursive call in isolation, it doesn't matter what the third fighter was because he will be replaced by our choice anyway 17 | * Considering also the "no fighter" option, we have 5 possible values and 2 positions, hence 25 possibilities 18 | * can map f1,f2 -> 5 * f1 + f2 (just like in matrix indexing) 19 | * in total for both queues, gives ~= 625 ~= 6e2 20 | 2. For the diff: 21 | * Given the constraint that at no round the excitement can be negative, we know that the abs(diff) is bounded by 11, as 2^12 = 4096 > 3000, as 3000 is the best excitement 22 | * Hence, map diff -> diff + 12 23 | * this dimension is thus ~= 24 ~= 2e1 large 24 | * Also, don't forget to stop recursion should we reach abs(diff) >= 12 25 | 26 | 27 | In total, for n <= 5000, we have 5000 * 6e2 * 2e1 ~= 6e7, so feasible enough! 28 | 29 | Note that there are other (iterative) solutions using a map and bitshifting to get an integer key, but IMO the classical recursion as here is intuitive. -------------------------------------------------------------------------------- /problems/week04-potw-fighting_pits_mereen/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | /// 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int n, k, m; 13 | typedef std::vector VI; 14 | typedef std::vector VVI; 15 | typedef std::vector VVVI; 16 | typedef std::vector VVVVI; 17 | 18 | struct Q 19 | { 20 | int f1; 21 | int f2; 22 | int f3; // three previous fighters 23 | }; 24 | 25 | struct key 26 | { 27 | int i1; 28 | int i2; 29 | int i3; 30 | }; 31 | 32 | int num_distinct(Q &q) 33 | { 34 | // use the no_fighter = 0 to easily incorporate the min(m, q) constraint 35 | if (m == 2) 36 | return std::set({0, q.f1, q.f2}).size() - 1; 37 | else 38 | return std::set({0, q.f1, q.f2, q.f3}).size() - 1; 39 | } 40 | 41 | Q insert(Q &q, int val) // drops the last value f3 42 | { 43 | return {val, q.f1, q.f2}; 44 | } 45 | 46 | key encode(Q &north, Q &south, int diff) // return three indices, i1, i2, diff <= 24 47 | { 48 | int k_n = 5 * north.f1 + north.f2; 49 | int k_s = 5 * south.f1 + south.f2; 50 | return {k_n, k_s, diff + 12}; 51 | } 52 | 53 | int excitement(bool use_north, Q &north, Q &south, int diff) 54 | { 55 | if (use_north) 56 | return (num_distinct(north) * 1000) - int(std::pow(2, std::abs(diff))); 57 | else 58 | return (num_distinct(south) * 1000) - int(std::pow(2, std::abs(diff))); 59 | } 60 | 61 | int solve( 62 | VVVVI &dp, int n, std::vector &v, Q &north, Q &south, int diff) 63 | { 64 | if (std::abs(diff) >= 12) // guaranteed to be negative value, so not feasible 65 | { 66 | return std::numeric_limits::min(); 67 | } 68 | if (n == -1) 69 | return 0; 70 | 71 | key k = encode(north, south, diff); 72 | if (dp[n][k.i1][k.i2][k.i3] != -1) 73 | { 74 | return dp[n][k.i1][k.i2][k.i3]; 75 | } 76 | int f = v[n]; 77 | Q new_north = insert(north, f); 78 | Q new_south = insert(south, f); 79 | // the excitement in this round for both choices 80 | int val_north = excitement(true, new_north, south, diff + 1); 81 | int val_south = excitement(false, north, new_south, diff - 1); 82 | 83 | int best = std::numeric_limits::min(); 84 | if (val_north >= 0) 85 | { 86 | best = val_north + solve(dp, n - 1, v, new_north, south, diff + 1); 87 | } 88 | if (val_south >= 0) 89 | { 90 | best = std::max(best, val_south + solve(dp, n - 1, v, north, new_south, diff - 1)); 91 | } 92 | dp[n][k.i1][k.i2][k.i3] = best; 93 | return best; 94 | } 95 | 96 | void testcase() 97 | { 98 | std::cin >> n >> k >> m; 99 | std::vector v(n); 100 | 101 | VVVVI dp(n, VVVI(25, VVI(25, VI(25, -1)))); 102 | for (int i = n - 1; i >= 0; --i) 103 | { 104 | std::cin >> v[i]; 105 | v[i]++; // increase so that 0 is reserved for no fighter type 106 | } 107 | Q init_empty = {0, 0, 0}; 108 | std::cout << solve(dp, n - 1, v, init_empty, init_empty, 0) << std::endl; 109 | } 110 | 111 | int main() 112 | { 113 | std::ios_base::sync_with_stdio(false); // Always! 114 | int t; 115 | std::cin >> t; 116 | for (int i = 0; i < t; i++) 117 | { 118 | testcase(); 119 | } 120 | } -------------------------------------------------------------------------------- /problems/week05-asterix_the_gaul/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week05-asterix_the_gaul/README.md: -------------------------------------------------------------------------------- 1 | Tags: Split and List, Subset Sum 2 | 3 | Key ideas: 4 | * Problem ressembles subset sum, with the specialty that we have a constraint sum of items (< T) while only getting a minimum certain amount (>= D) 5 | * --> NP-hard problem, which is why the instances are very small (<= 30) 6 | * for m=0: 7 | * just enumerate all possible subsets, calculate there sums and check if they satisfy constraints < T and >= D 8 | * I took the code from the subset sum tutorial slides: the recursive version (which works for testsets 1-3) and an iterative version using bitshifts 9 | * For larger m: 10 | * We have the invariant that if we take gulp s_j and we have a solution, then we also have a solution for j' >= j 11 | * hence: can do binary search to find the smallest j, for each choice s_j add the distance to all elements, later subtracting it again 12 | ``` 13 | while(l<=r) 14 | mid = (l+r)/2 15 | if(subset_sum_exists) { 16 | r = mid; 17 | if(l == r) break; 18 | } else { 19 | l = mid + 1; 20 | } 21 | ``` 22 | * For latest testcase: need split and list! 23 | * split the set into two roughly equally sized sets 24 | * compute the sums of d and sums of t for each set 25 | * if there already is a solution, can return true 26 | * sort the 2nd set sums after the time, then do binary search for each value in first set 27 | * important step: for increasing t, use the maximum sum of d over the smaller t' 28 | * why? when doing the binary search, we look for the _largest_ t that still fits with sum < T, but we want to maximize the sum D. hence, copy over the max sum_D for t' < t -------------------------------------------------------------------------------- /problems/week05-boats/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week05-boats/README.md -------------------------------------------------------------------------------- /problems/week05-boats/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | struct sort_p { 8 | bool operator() (pair t0, pair t1) { 9 | return (t0.second < t1.second) ; 10 | } 11 | }; 12 | 13 | void testcase() { 14 | int n; cin >> n; 15 | vector> boats(n); 16 | for(int i = 0; i < n; i++) { 17 | cin >> boats[i].first; cin >> boats[i].second; 18 | } 19 | std::sort(boats.begin(), boats.end(), sort_p()); 20 | if(n == 1) { 21 | cout << 1 << endl; 22 | } 23 | int num_boats = 2; 24 | int l = boats[1].first; 25 | int p = boats[1].second; 26 | int previous_blocked = boats[0].second; 27 | int curr_blocked = max(previous_blocked + l, p); 28 | for(int i = 2; i < n; i++) { 29 | l = boats[i].first; 30 | p = boats[i].second; 31 | // cout << "l " << l << endl; 32 | // cout << "p " << p << endl; 33 | if(curr_blocked <= p - l) { // can put curr boat leftmost 34 | previous_blocked = curr_blocked; 35 | curr_blocked = p; 36 | num_boats++; 37 | } else if(curr_blocked <= p) { // can put boat at end of previous boat 38 | previous_blocked = curr_blocked; 39 | curr_blocked = curr_blocked + l; 40 | num_boats++; 41 | } else { 42 | int taking_this_blocked = max(previous_blocked + l, p); 43 | if(taking_this_blocked < curr_blocked) { // can swap last boat with this one and go less with current blocking pos 44 | curr_blocked = taking_this_blocked; 45 | } 46 | } 47 | } 48 | 49 | cout << num_boats << endl; 50 | } 51 | 52 | int main() { 53 | std::ios_base::sync_with_stdio(false); // Always! 54 | int t; cin >> t; 55 | while(t--) testcase(); 56 | } -------------------------------------------------------------------------------- /problems/week05-moving_books/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week05-moving_books/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week05-moving_books/README.md -------------------------------------------------------------------------------- /problems/week05-moving_books/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | /// 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | void testcase() { 11 | int n; cin >> n; 12 | int m; cin >> m; 13 | int max_strength = 0; 14 | int max_weight = 0; 15 | vector strength(n); 16 | vector weight(m); 17 | for(int i = 0; i < n; i++) { 18 | cin >> strength[i]; 19 | max_strength = max(max_strength, strength[i]); 20 | } 21 | for(int i = 0; i < m; i++) { 22 | cin >> weight[i]; 23 | max_weight = max(max_weight, weight[i]); 24 | } 25 | 26 | if(max_weight > max_strength) { 27 | cout << "impossible" << endl; 28 | return; 29 | } 30 | 31 | std::sort(strength.begin(), strength.end(), std::greater<>()); 32 | 33 | /* master solution 34 | int max_num_boxes = 0; 35 | multiset> weights_set; 36 | for(int i = 0; i < m; i++) 37 | weights_set.insert(weight[i]); 38 | 39 | while(!weights_set.empty()) { 40 | max_num_boxes++; 41 | for(int i = 0; i < n; i++) { 42 | auto b = weights_set.lower_bound(strength[i]); 43 | if(b != weights_set.end()) { 44 | weights_set.erase(b); 45 | } else { 46 | break; 47 | } 48 | } 49 | } 50 | */ 51 | 52 | // My solution 53 | std::sort(weight.begin(), weight.end(), std::greater<>()); 54 | vector num_boxes(n, 0); 55 | int max_num_boxes = 1; 56 | num_boxes[0] = 1; // start with first guy carrying heaviest box 57 | for(int i = 1; i < m; i++) { 58 | int weight_i = weight[i]; 59 | // the index of the _last_ guy that is next in line to carry it 60 | auto next = std::upper_bound(num_boxes.begin(), num_boxes.end(), max_num_boxes, std::greater<>()); 61 | // the index of the first guy that is _too weak_ to carry it 62 | auto end = std::upper_bound(strength.begin(), strength.end(), weight_i, std::greater<>()); 63 | int next_index = next - num_boxes.begin(); 64 | int end_index = end - strength.begin(); 65 | // cerr << i << " " << next_index << " " << end_index << endl; 66 | if(next_index < end_index) { 67 | num_boxes[next_index]++; 68 | } else { 69 | num_boxes[0]++; 70 | max_num_boxes = num_boxes[0]; 71 | } 72 | } 73 | 74 | int min_time = (max_num_boxes - 1) * 3 + 2; 75 | cout << min_time << endl; 76 | } 77 | 78 | int main() { 79 | std::ios_base::sync_with_stdio(false); // Always! 80 | int t; cin >> t; 81 | while(t--) testcase(); 82 | } -------------------------------------------------------------------------------- /problems/week05-potw-motorcycles/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week05-potw-motorcycles/README.md -------------------------------------------------------------------------------- /problems/week05-potw-motorcycles/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | /// 2 | #include 3 | #include 4 | typedef CGAL::Exact_predicates_exact_constructions_kernel K; 5 | typedef K::Point_2 P; 6 | typedef std::tuple T4; 7 | typedef CGAL::Gmpq slope; 8 | using namespace std; 9 | 10 | 11 | struct T4_y0_descending { 12 | bool operator() (const T4& t0, const T4& t1) { 13 | return get<0>(t0) > get<0>(t1); 14 | } 15 | }; 16 | 17 | 18 | void testcase() 19 | { 20 | int n; std::cin >> n; 21 | vector rides_forever(n, true); 22 | vector indices(n); 23 | for(int i = 0; i < n; ++i) { 24 | long y0, x1, y1; cin >> y0; cin >> x1; cin >> y1; 25 | indices[i] = T4(y0, x1, y1, i); 26 | } 27 | 28 | std::sort(indices.begin(), indices.end(), T4_y0_descending()); 29 | 30 | T4 curr_line = indices[0]; 31 | slope y0 = (slope) get<0>(curr_line); 32 | slope x1 = (slope) get<1>(curr_line); 33 | slope y1 = (slope) get<2>(curr_line); 34 | slope min_abs_slope = (y1 - y0) / x1; 35 | 36 | for(int i = 1; i < n; i++) { // mark slopes going up in downpass 37 | curr_line = indices[i]; 38 | y0 = (slope) get<0>(curr_line); 39 | x1 = (slope) get<1>(curr_line); 40 | y1 = (slope) get<2>(curr_line); 41 | int j = get<3>(curr_line); 42 | slope curr_slope = (y1 - y0) / x1; 43 | if(CGAL::abs(curr_slope) <= CGAL::abs(min_abs_slope)) 44 | min_abs_slope = curr_slope; 45 | else if(curr_slope > min_abs_slope) 46 | rides_forever[j] = false; 47 | } 48 | 49 | // curr_line = indices[n - 1]; 50 | min_abs_slope = (y1 - y0) / x1; 51 | 52 | for(int i = n - 2; i >= 0; i--) { // mark slopes going down in uppass 53 | curr_line = indices[i]; 54 | y0 = (slope) get<0>(curr_line); 55 | x1 = (slope) get<1>(curr_line); 56 | y1 = (slope) get<2>(curr_line); 57 | int j = get<3>(curr_line); 58 | slope curr_slope = (y1 - y0) / x1; 59 | if(CGAL::abs(curr_slope) < CGAL::abs(min_abs_slope) 60 | || (CGAL::abs(curr_slope) == CGAL::abs(min_abs_slope) 61 | && curr_slope > min_abs_slope)) 62 | min_abs_slope = curr_slope; 63 | else if(curr_slope < min_abs_slope) 64 | rides_forever[j] = false; 65 | } 66 | 67 | 68 | for(int i = 0; i < n; i++ ) { 69 | if(rides_forever[i]) cout << i << " "; 70 | } 71 | cout << endl; 72 | } 73 | 74 | int main() 75 | { 76 | std::ios_base::sync_with_stdio(false); // Always! 77 | int t; cin >> t; 78 | for(int i = 0; i < t; i++) 79 | testcase(); 80 | return 0; 81 | } -------------------------------------------------------------------------------- /problems/week05-severus_snape/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week05-severus_snape/README.md: -------------------------------------------------------------------------------- 1 | Tags: Dynamic Programming, Greedy Search 2 | 3 | Key ideas: 4 | * A combination of Dynamic Programming/Precomputation and then greedy search is needed here. 5 | * First: 6 | * Note that since the potions of A have 2 different values, they do not have a unique greedy ordering 7 | * The potions B, however, have one. Just sort after their value w_i 8 | * Apart from the first test case, sorting potions A thus has no value 9 | * Then we can make the observation: both the number of potions (n <= 100) and the value H are relatively small. Additionally the value H is not negatively affected by potions B 10 | * We can thus make a DP/memory table that still fits in memory and has not too expensive runtime: 11 | * define dp[i][j][h] as the _largest_ sum of power we can get when taking *j* out of the first *i* potions that have a sum of at least *h* 12 | * size: 100 ^2 * 2^10 ~= 10^7 13 | * initialised with all -inf (use long because the sum of powers can be too big for integer) 14 | * base cases: 15 | * for all h, for i>=1, j==1: 16 | * take the highest power potions that fulfills h 17 | * dp[i][1][h] = pots_a[i-1].h >= h ? max(dp[i-1][1][h], pots_a[i - 1].p) : dp[i-1][1][h]; 18 | * then, increasing j>=2, h, i>=j: 19 | * either the previous value without potions i was better, or we take it and can add its power to the value of i-1, j-1, and fulfilling the h-offest 20 | * int prev_h = max(0, h - pots_a[i-1].h); 21 | * dp[i][j][h] = max(dp[i-1][j][h], dp[i-1][j-1][prev_h] + pots_a[i-1].p); 22 | * Having filled out the DP table, we e.g. know that we can search for the smallest j for which dp[n][j][H]>=P in order to have the minimum of potions to fulfill P and H 23 | 24 | To combine it with the potions of B, do a greedy search: 25 | * We take more and more potions B (sorted by their w_i), compute the current wit w that we get as well as the decrease in power via (#pot * b) 26 | * For a certain number of potions, we search for increasing j in our DP table: 27 | * We cannot take too many j that decrease the current wit too much (offset j * a) 28 | * While wit is okay, we search for the first j where dp[n][j][H] >= P + (#potB * b) 29 | * We know that the first time this conditions if fulfilled, we have the minimum potions! That is because we increase the #potB and look for the smallest j for each iteration - there cannot be a smaller sum of those values in a later iteration -------------------------------------------------------------------------------- /problems/week05-severus_snape/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | struct A { 11 | int p; 12 | int h; 13 | int i; 14 | }; 15 | 16 | struct B { 17 | int w; 18 | int i; 19 | }; 20 | 21 | typedef std::vector VL; 22 | typedef std::vector VVL; 23 | typedef std::vector VVVL; 24 | 25 | void testcase() { 26 | int n, m; 27 | long a, b, P, H, W; 28 | std::cin >> n >> m >> a >> b >> P >> H >> W; 29 | 30 | std::vector pots_a(n); 31 | std::vector pots_b(m); 32 | 33 | for(int i = 0; i < n; i++) { 34 | int p, h; 35 | std::cin >> p >> h; 36 | pots_a[i] = {p, h, i}; 37 | } 38 | 39 | for(int i = 0; i < m; i++) { 40 | int w; 41 | std::cin >> w; 42 | pots_b[i] = {w, i}; 43 | } 44 | // order of B potions is definite as they only have 1 unique val 45 | std::sort(pots_b.begin(), pots_b.end(), [](const B& b1, const B& b2) -> bool { 46 | return b1.w > b2.w; 47 | }); 48 | 49 | // use long bc sum of powers can be larger than 2^32 50 | long l_min = std::numeric_limits::min(); 51 | VVVL dp(n + 1, VVL(n + 1, VL(H + 1, l_min))); 52 | // dp[i][j][h] describes the maximum amount of power one can get 53 | // with the first i potions, using exactly j of them to have at least happiness h 54 | 55 | // init for using a single potions, just take the highest p over those that satisfy h 56 | for(int h = 0; h <= H; h++) { 57 | for(int i = 1; i <= n; i++) { 58 | dp[i][1][h] = dp[i-1][1][h]; 59 | if(pots_a[i-1].h >= h && dp[i][1][h] < pots_a[i - 1].p) 60 | dp[i][1][h] = pots_a[i-1].p; 61 | // std::cerr << "i=" << i << " j=" << 1 << " h=" << h << " dp=" << dp[i][1][h] << std::endl; 62 | } 63 | } 64 | // std::cout << "--------------" << std::endl; 65 | for(int j = 2; j <= n; j++) { 66 | for(int h = 0; h <= H; h++) { 67 | for(int i = j; i <= n; i++) { 68 | // either take this one or take previous value 69 | int prev_h = std::max(0, h - pots_a[i-1].h); 70 | dp[i][j][h] = std::max(dp[i-1][j][h], dp[i-1][j-1][prev_h] + pots_a[i-1].p); 71 | // std::cerr << "i=" << i << " j=" << j << " h=" << h << " dp=" << dp[i][j][h] << std::endl; 72 | } 73 | } 74 | } 75 | 76 | int num_potions_a = -1; 77 | int num_potions_b = 0; 78 | long curr_wit = 0; 79 | // greedily take potions of B and try to find the _least_ number of potions A 80 | // that together give us the power and wit 81 | for(; num_potions_b < m; num_potions_b++) { 82 | curr_wit += pots_b[num_potions_b].w; 83 | long neg_power = (num_potions_b + 1) * b; 84 | if(curr_wit >= W) { 85 | // curr_wit suffices to have W, look for smallest j that gives us all 86 | for(int j = 1; j <= n; j++) { 87 | if(curr_wit - j * a < W) { 88 | break; 89 | } 90 | if(dp[n][j][H] >= P + neg_power) { 91 | num_potions_a = j; 92 | // std::cerr << num_potions_a << " " << num_potions_b + 1 << std::endl; 93 | std::cout << num_potions_a + num_potions_b + 1 << std::endl; 94 | return; 95 | } 96 | } 97 | } 98 | } 99 | 100 | if(num_potions_a == -1) { 101 | std::cout << -1 << std::endl; 102 | return; 103 | } 104 | 105 | return; 106 | } 107 | 108 | int main() { 109 | std::ios_base::sync_with_stdio(false); 110 | 111 | int t; 112 | std::cin >> t; 113 | for (int i = 0; i < t; ++i) 114 | testcase(); 115 | } 116 | -------------------------------------------------------------------------------- /problems/week06-diet/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week06-diet/README.md -------------------------------------------------------------------------------- /problems/week06-diet/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | /// 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // choose input type (input coefficients must fit) 8 | typedef int IT; 9 | // choose exact type for solver (CGAL::Gmpz or CGAL::Gmpq) 10 | typedef CGAL::Gmpz ET; 11 | 12 | // program and solution types 13 | typedef CGAL::Quadratic_program Program; 14 | typedef CGAL::Quadratic_program_solution Solution; 15 | 16 | using namespace std; 17 | 18 | 19 | int floor_to_int(const CGAL::Quotient & x) 20 | { 21 | int a = std::floor(CGAL::to_double(x)); 22 | while (a > x) a -= 1; 23 | while (a+1 <= x) a += 1; 24 | return a; 25 | } 26 | 27 | 28 | int ceil_to_int(const CGAL::Quotient & x) 29 | { 30 | double a = std::ceil(CGAL::to_double(x)); 31 | while (a+1 >= x) a -= 1; 32 | while (a < x) a += 1; 33 | return a; 34 | } 35 | 36 | void testcases() 37 | { 38 | 39 | // set the coefficients of A and b 40 | Program lp; 41 | int n, m; 42 | while(true) { 43 | cin >> n; cin >> m; 44 | if(n == 0 && m == 0) { 45 | return; 46 | } else { 47 | // create an LP with Ax <= b, lower bound 0 and no upper bounds 48 | lp = Program(CGAL::SMALLER, true, 0, false, 0); 49 | vector prices(m); 50 | vector> nutrients(n); 51 | for(int i = 0; i < n; i++) { 52 | cin >> nutrients[i].first; cin >> nutrients[i].second; 53 | lp.set_b(i, nutrients[i].second); 54 | lp.set_b(i + n, -nutrients[i].first); 55 | } 56 | for(int j = 0; j < m; j++) { 57 | cin >> prices[j]; 58 | for(int i = 0; i < n; i++) { 59 | int C_ji; cin >> C_ji; 60 | lp.set_a(j, i, C_ji); // x^T * C[:,i] <= max_i 61 | lp.set_a(j, i + n, -C_ji); // -x^T * C[:,i] <= min_i 62 | } 63 | // objective function 64 | lp.set_c(j, prices[j]); 65 | } 66 | // solve the program, using ET as the exact type 67 | Solution s = CGAL::solve_linear_program(lp, ET()); 68 | if(s.is_unbounded()) { 69 | cout << "unbounded" << endl; 70 | } else if(s.is_infeasible()) { 71 | cout << "No such diet." << endl; 72 | } else { 73 | auto val = s.objective_value(); 74 | // cerr << s << endl; 75 | cout << floor_to_int(val) << endl; 76 | } 77 | } 78 | } 79 | } 80 | 81 | int main() { 82 | std::ios_base::sync_with_stdio(false); // Always! 83 | testcases(); 84 | } 85 | 86 | -------------------------------------------------------------------------------- /problems/week06-inball/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week06-inball/README.md: -------------------------------------------------------------------------------- 1 | Tags: Linear Programming, Geometry 2 | 3 | Key ideas: 4 | * distance between point and a line in d-dimensions is r = (a^T * x - b)/norm(a) for a line that is given by the linear constraints a^T * x = b. See, for instance, that the equality holds when r = 0. 5 | * Since we have exactly this form here of a^T * x <= b , we can set the look for the center of the circle while maximizing the radius r, which gives the constraints: 6 | * a^T * x + r * norm(a) <= b -------------------------------------------------------------------------------- /problems/week06-inball/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // choose input type (input coefficients must fit) 7 | typedef long IT; 8 | // choose exact type for solver (CGAL::Gmpz or CGAL::Gmpq) 9 | typedef CGAL::Gmpz ET; 10 | 11 | // program and solution types 12 | typedef CGAL::Quadratic_program Program; 13 | typedef CGAL::Quadratic_program_solution Solution; 14 | 15 | using namespace std; 16 | 17 | 18 | int floor_to_int(const CGAL::Quotient & x) 19 | { 20 | int a = std::floor(CGAL::to_double(x)); 21 | while (a > x) a -= 1; 22 | while (a+1 <= x) a += 1; 23 | return a; 24 | } 25 | 26 | 27 | int ceil_to_int(const CGAL::Quotient & x) 28 | { 29 | double a = std::ceil(CGAL::to_double(x)); 30 | while (a+1 >= x) a -= 1; 31 | while (a < x) a += 1; 32 | return a; 33 | } 34 | 35 | void testcase() 36 | { 37 | 38 | // set the coefficients of A and b 39 | Program lp; 40 | int n, d; 41 | while(true) { 42 | cin >> n; 43 | if(n == 0) { 44 | return; 45 | } 46 | cin >> d; 47 | 48 | lp = Program(CGAL::SMALLER, false, 0, false, 0); 49 | for(int i = 0; i < n; i++) { 50 | vector curr_constraints(d); 51 | long curr_len = 0; 52 | for(int j = 0; j < d; j++) { 53 | int a_ij; cin >> a_ij; 54 | curr_constraints[j] = a_ij; 55 | curr_len += a_ij * a_ij; 56 | lp.set_a(j, i, IT(a_ij)); 57 | } 58 | curr_len = sqrt(curr_len); 59 | 60 | int b_i; cin >> b_i; 61 | lp.set_b(i, IT(b_i)); 62 | lp.set_a(d, i, IT(curr_len)); 63 | } 64 | 65 | lp.set_l(d, true, 0); // radius nonnegative 66 | lp.set_c(d, -1); // maximize radius (i.e. minimize inverse) 67 | // solve the program, using ET as the exact type 68 | Solution s = CGAL::solve_linear_program(lp, ET()); 69 | if(s.is_unbounded()) { 70 | cout << "inf" << endl; 71 | } else if(s.is_infeasible()) { 72 | cout << "none" << endl; 73 | } else { 74 | // cerr << s << endl; 75 | auto val = s.objective_value(); 76 | // actually useless here to round since its an integer, anyway 77 | cout << floor_to_int(-val) << endl; 78 | } 79 | } 80 | } 81 | 82 | int main() { 83 | std::ios_base::sync_with_stdio(false); // Always! 84 | testcase(); 85 | } 86 | 87 | -------------------------------------------------------------------------------- /problems/week06-lannister/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week06-lannister/README.md -------------------------------------------------------------------------------- /problems/week06-maximize_it/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week06-maximize_it/README.md -------------------------------------------------------------------------------- /problems/week06-maximize_it/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // example: how to solve a simple explicit LP 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // choose input type (input coefficients must fit) 8 | typedef int IT; 9 | // choose exact type for solver (CGAL::Gmpz or CGAL::Gmpq) 10 | typedef CGAL::Gmpz ET; 11 | 12 | // program and solution types 13 | typedef CGAL::Quadratic_program Program; 14 | typedef CGAL::Quadratic_program_solution Solution; 15 | 16 | using namespace std; 17 | 18 | 19 | int floor_to_int(const CGAL::Quotient & x) 20 | { 21 | int a = std::floor(CGAL::to_double(x)); 22 | while (a > x) a -= 1; 23 | while (a+1 <= x) a += 1; 24 | return a; 25 | } 26 | 27 | 28 | int ceil_to_int(const CGAL::Quotient & x) 29 | { 30 | double a = std::ceil(CGAL::to_double(x)); 31 | while (a+1 >= x) a -= 1; 32 | while (a < x) a += 1; 33 | return a; 34 | } 35 | 36 | void testcases() 37 | { 38 | 39 | // set the coefficients of A and b 40 | const int X = 0; 41 | const int Y = 1; 42 | const int Z = 2; 43 | Program lp; 44 | int p, a, b; 45 | while(true) { 46 | cin >> p; 47 | if(p == 0) { 48 | return; 49 | } else if(p == 1) { // prob 1 50 | cin >> a; cin >> b; 51 | // create an LP with Ax <= b, lower bound 0 and no upper bounds 52 | lp = Program(CGAL::SMALLER, true, 0, false, 0); 53 | lp.set_a(X, 0, 1); lp.set_a(Y, 0, 1); lp.set_b(0, 4); // x + y <= 4 54 | lp.set_a(X, 1, 4); lp.set_a(Y, 1, 2); lp.set_b(1, a * b); // 4x + 2y <= ab 55 | lp.set_a(X, 2, -1); lp.set_a(Y, 2, 1); lp.set_b(2, 1); // -x + y <= 1 56 | // objective function, minimize by flipping signs 57 | lp.set_c(X, a); // ax 58 | lp.set_c(Y, -b); // -by 59 | } else { 60 | cin >> a; cin >> b; 61 | 62 | // create an LP with Ax <= b, lower bound 0 and no upper bounds 63 | lp = Program(CGAL::SMALLER, false, 0, true, 0); 64 | lp.set_a(X, 0, -1); lp.set_a(Y, 0, -1);// lp.set_a(Z, 0, 0); 65 | lp.set_b(0, 4); // -x - y <= 4 66 | lp.set_a(X, 1, -4); lp.set_a(Y, 1, -2); lp.set_a(Z, 1, -1); lp.set_b(1, a * b); // 4x + 2y <= ab 67 | lp.set_a(X, 2, 1); lp.set_a(Y, 2, -1); //lp.set_a(Z, 2, 0); 68 | lp.set_b(2, 1); // x - y <= 1 69 | // objective function, minimize by flipping signs 70 | lp.set_c(X, a); // ax 71 | lp.set_c(Y, b); // by 72 | lp.set_c(Z, 1); // z 73 | 74 | } 75 | 76 | // solve the program, using ET as the exact type 77 | Solution s = CGAL::solve_linear_program(lp, ET()); 78 | if(s.is_unbounded()) { 79 | cout << "unbounded" << endl; 80 | } else if(s.is_infeasible()) { 81 | cout << "no" << endl; 82 | } else { 83 | auto val = s.objective_value(); 84 | if(p == 1) { // max problem 85 | cerr << s << endl; 86 | cout << -ceil_to_int(val) << endl; 87 | } else { // min problem 88 | cout << ceil_to_int(val) << endl; 89 | } 90 | } 91 | } 92 | } 93 | 94 | 95 | int main() { 96 | std::ios_base::sync_with_stdio(false); // Always! 97 | testcases(); 98 | } 99 | 100 | -------------------------------------------------------------------------------- /problems/week06-potw-planet_express/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week06-potw-planet_express/README.md -------------------------------------------------------------------------------- /problems/week06-potw-planet_express/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // STL includes 3 | #include 4 | #include 5 | 6 | // BGL includes 7 | #include 8 | #include 9 | #include 10 | 11 | typedef boost::adjacency_list > weighted_graph; 13 | typedef boost::property_map::type weight_map; 14 | typedef boost::graph_traits::edge_descriptor edge_desc; 15 | typedef boost::graph_traits::vertex_descriptor vertex_desc; 16 | 17 | using namespace std; 18 | 19 | int dijkstra(const weighted_graph &G, int s, int k) { 20 | int n = boost::num_vertices(G); 21 | std::vector dist_map(n); 22 | 23 | boost::dijkstra_shortest_paths(G, s, 24 | boost::distance_map(boost::make_iterator_property_map( 25 | dist_map.begin(), boost::get(boost::vertex_index, G)))); 26 | int min_dist = dist_map[0]; 27 | for(int i = 1; i < k; i++) { 28 | min_dist = min(min_dist, dist_map[i]); 29 | } 30 | return min_dist; 31 | } 32 | 33 | 34 | void testcase() { 35 | int n; cin >> n; 36 | int m; cin >> m; 37 | int k; cin >> k; 38 | int T; cin >> T; 39 | vector tele_nodes(T); 40 | for(int i = 0; i < T; i++) { 41 | cin >> tele_nodes[i]; 42 | } 43 | weighted_graph G(n); 44 | 45 | weight_map weights = boost::get(boost::edge_weight, G); 46 | edge_desc e; 47 | for(int i = 0; i < m; i++) { 48 | int u,v; cin >> u; cin >> v; 49 | int c; cin >> c; 50 | e = boost::add_edge(v, u, G).first; weights[e] = c; // flipped edges! 51 | } 52 | // scc_map[i]: index of SCC containing i-th vertex 53 | vector scc_map(n); // exterior property map 54 | // nscc: total number of SCCs 55 | int nscc = boost::strong_components(G, 56 | boost::make_iterator_property_map(scc_map.begin(), 57 | boost::get(boost::vertex_index, G))); 58 | 59 | vector size_scc(nscc); 60 | for(int i = 0; i < T; i++) { 61 | size_scc[scc_map[tele_nodes[i]]]++; 62 | } 63 | 64 | 65 | for(int i = 0; i < T; i++) { 66 | int u = tele_nodes[i]; 67 | int scc = scc_map[u]; 68 | int size_this_scc = size_scc[scc] - 1; 69 | e = boost::add_edge(u, n + scc, G).first; 70 | weights[e] = size_this_scc; 71 | e = boost::add_edge(n + scc, u, G).first; 72 | weights[e] = 0; 73 | } 74 | 75 | int min_dist_to_warehouse = dijkstra(G, n - 1, k); 76 | if(min_dist_to_warehouse <= 1e6) { 77 | cout << min_dist_to_warehouse << endl; 78 | } else { 79 | cout << "no" << endl; 80 | } 81 | 82 | } 83 | int main() 84 | { 85 | std::ios_base::sync_with_stdio(false); // Always! 86 | int t; cin >> t; 87 | for(int i = 0; i < t; i++) 88 | testcase(); 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /problems/week07-coin_tossing/README.md: -------------------------------------------------------------------------------- 1 | Tags: MaxFlow 2 | 3 | Key ideas: 4 | * Simple bipartite flow, connecting matches and winning player 5 | * c distinguishes the edges from match to players 6 | * flow has to be the sum of scoreboard and the number of matches m -------------------------------------------------------------------------------- /problems/week07-coin_tossing/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // Algolab BGL Tutorial 2 (Max flow, by taubnert@ethz.ch) 2 | // Flow example demonstrating how to use push_relabel_max_flow using a custom edge adder 3 | // to manage the interior graph properties required for flow algorithms 4 | #include 5 | 6 | // BGL include 7 | #include 8 | 9 | // BGL flow include *NEW* 10 | #include 11 | 12 | // Graph Type with nested interior edge properties for flow algorithms 13 | typedef boost::adjacency_list_traits traits; 14 | typedef boost::adjacency_list>>> graph; 18 | 19 | typedef traits::vertex_descriptor vertex_desc; 20 | typedef traits::edge_descriptor edge_desc; 21 | 22 | using namespace std; 23 | 24 | // Custom edge adder class, highly recommended 25 | class edge_adder { 26 | graph &G; 27 | 28 | public: 29 | explicit edge_adder(graph &G) : G(G) {} 30 | 31 | void add_edge(int from, int to, long capacity) { 32 | auto c_map = boost::get(boost::edge_capacity, G); 33 | auto r_map = boost::get(boost::edge_reverse, G); 34 | const auto e = boost::add_edge(from, to, G).first; 35 | const auto rev_e = boost::add_edge(to, from, G).first; 36 | c_map[e] = capacity; 37 | c_map[rev_e] = 0; // reverse edge has no capacity! 38 | r_map[e] = rev_e; 39 | r_map[rev_e] = e; 40 | } 41 | }; 42 | 43 | void make_it_flow() { 44 | int n, m; 45 | cin >> n; cin >> m; 46 | graph G(n + m); 47 | edge_adder adder(G); 48 | 49 | // Add special vertices source and sink 50 | const vertex_desc v_source = boost::add_vertex(G); 51 | const vertex_desc v_sink = boost::add_vertex(G); 52 | 53 | for(int i = 0; i < m; i++) { 54 | adder.add_edge(v_source, i, 1); // from, to, capacity 55 | int a, b, c; 56 | cin >> a; cin >> b; cin >> c; 57 | if(c == 1) { 58 | adder.add_edge(i, m + a, 1); // from, to, capacity 59 | } else if(c == 2) { 60 | adder.add_edge(i, m + b, 1); // from, to, capacity 61 | } else { 62 | adder.add_edge(i, m + a, 1); // from, to, capacity 63 | adder.add_edge(i, m + b, 1); // from, to, capacity 64 | } 65 | } 66 | 67 | int sum_s = 0; 68 | for(int i = 0; i < n; i++) { 69 | int si; cin >> si; 70 | adder.add_edge(m + i, v_sink, si); 71 | sum_s += si; 72 | } 73 | 74 | 75 | // Calculate flow from source to sink 76 | // The flow algorithm uses the interior properties (managed in the edge adder) 77 | // - edge_capacity, edge_reverse (read access), 78 | // - edge_residual_capacity (read and write access). 79 | long flow = boost::push_relabel_max_flow(G, v_source, v_sink); 80 | 81 | if(flow == sum_s && flow == m) { 82 | std::cout << "yes" << "\n"; 83 | } else { 84 | std::cout << "no" << "\n"; 85 | } 86 | } 87 | 88 | int main() { 89 | std::ios_base::sync_with_stdio(false); // Always! 90 | int t; cin >> t; 91 | while(t--) make_it_flow(); 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /problems/week07-knights/README.md: -------------------------------------------------------------------------------- 1 | Tags: MaxFlow 2 | 3 | Key ideas: 4 | * Edge distinct paths to outside with vertex capacities -------------------------------------------------------------------------------- /problems/week07-knights/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | ///1 2 | // Algolab BGL Tutorial 2 (Max flow, by taubnert@ethz.ch) 3 | // Flow example demonstrating how to use push_relabel_max_flow using a custom edge adder 4 | // to manage the interior graph properties required for flow algorithms 5 | #include 6 | 7 | // BGL include 8 | #include 9 | 10 | // BGL flow include *NEW* 11 | #include 12 | 13 | // Graph Type with nested interior edge properties for flow algorithms 14 | typedef boost::adjacency_list_traits traits; 15 | typedef boost::adjacency_list>>> graph; 19 | 20 | typedef traits::vertex_descriptor vertex_desc; 21 | typedef traits::edge_descriptor edge_desc; 22 | 23 | using namespace std; 24 | 25 | // Custom edge adder class, highly recommended 26 | class edge_adder { 27 | graph &G; 28 | 29 | public: 30 | explicit edge_adder(graph &G) : G(G) {} 31 | 32 | void add_edge(int from, int to, long capacity) { 33 | auto c_map = boost::get(boost::edge_capacity, G); 34 | auto r_map = boost::get(boost::edge_reverse, G); 35 | const auto e = boost::add_edge(from, to, G).first; 36 | const auto rev_e = boost::add_edge(to, from, G).first; 37 | c_map[e] = capacity; 38 | c_map[rev_e] = 0; // reverse edge has no capacity! 39 | r_map[e] = rev_e; 40 | r_map[rev_e] = e; 41 | } 42 | }; 43 | 44 | void make_it_flow() { 45 | int n, m, k, c; 46 | cin >> m; cin >> n; cin >> k; cin >> c; 47 | graph G(n * m * 2); 48 | edge_adder adder(G); 49 | 50 | 51 | // Add special vertices source and sink 52 | const vertex_desc v_source = boost::add_vertex(G); 53 | const vertex_desc v_sink = boost::add_vertex(G); 54 | 55 | for(int i = 0; i < m; i++) { 56 | for(int j = 0; j < n; j++) { 57 | // copy of in-node: i * n + j, out-node: (n * m) + (i * n + j) 58 | int in_copy = i * n + j; 59 | int out_copy = n * m + in_copy; 60 | int num_exits = 0; 61 | if(i == 0) { // first row, can exit to sink 62 | num_exits++; 63 | } else { // else add edge up 64 | adder.add_edge(out_copy, (i - 1) * n + j, 1); 65 | } 66 | 67 | if(i == m - 1) { // last row, can exit to sink 68 | num_exits++; 69 | } else { // else add edge down 70 | adder.add_edge(out_copy, (i + 1) * n + j, 1); 71 | } 72 | 73 | if(j == 0) { // first column, can exit to sink 74 | num_exits++; 75 | } else { // else add edge left 76 | adder.add_edge(out_copy, i * n + j - 1, 1); 77 | } 78 | 79 | if(j == n - 1) { // last column, can exit to sink 80 | num_exits++; 81 | } else { // else add edge down 82 | adder.add_edge(out_copy, i * n + j + 1, 1); 83 | } 84 | 85 | if(num_exits > 0) { 86 | adder.add_edge(out_copy, v_sink, num_exits); 87 | } 88 | adder.add_edge(in_copy, out_copy, c); // from, to, capacity 89 | } 90 | } 91 | 92 | for(int i = 0; i < k; i++) { 93 | int x, y; cin >> x; cin >> y; 94 | adder.add_edge(v_source, x * n + y, 1); 95 | } 96 | 97 | // Calculate flow from source to sink 98 | // The flow algorithm uses the interior properties (managed in the edge adder) 99 | // - edge_capacity, edge_reverse (read access), 100 | // - edge_residual_capacity (read and write access). 101 | long flow = boost::push_relabel_max_flow(G, v_source, v_sink); 102 | 103 | std::cout << flow << "\n"; 104 | 105 | // // Retrieve the capacity map and reverse capacity map 106 | // const auto c_map = boost::get(boost::edge_capacity, G); 107 | // const auto rc_map = boost::get(boost::edge_residual_capacity, G); 108 | 109 | // // Iterate over all the edges to print the flow along them 110 | // auto edge_iters = boost::edges(G); 111 | // for (auto edge_it = edge_iters.first; edge_it != edge_iters.second; ++edge_it) { 112 | // const edge_desc edge = *edge_it; 113 | // const long flow_through_edge = c_map[edge] - rc_map[edge]; 114 | // std::cout << "edge from " << boost::source(edge, G) << " to " << boost::target(edge, G) 115 | // << " runs " << flow_through_edge 116 | // << " units of flow (negative for reverse direction). \n"; 117 | // } 118 | } 119 | 120 | int main() { 121 | std::ios_base::sync_with_stdio(false); // Always! 122 | int t; cin >> t; 123 | while(t--) make_it_flow(); 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /problems/week07-london/README.md: -------------------------------------------------------------------------------- 1 | Tags: MaxFlow 2 | 3 | Key ideas: 4 | * 26*26+26 nodes, bipartite 5 | * first 26*26 are for front/back pairs, and they are connected to the two letters (2nd set) they have 6 | * each letter has demand 7 | * each pair has supply -------------------------------------------------------------------------------- /problems/week07-potw-octopussy/README.md: -------------------------------------------------------------------------------- 1 | Tags: Greedy 2 | 3 | Key ideas: 4 | * Sort bombs after time, try to deactivate them in that order 5 | * recursively: if bomb not at bottom, deactivate its "children" first -------------------------------------------------------------------------------- /problems/week07-potw-octopussy/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | /// 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | typedef pair pd; 11 | 12 | struct myComp { 13 | bool operator()( 14 | pd const& a, 15 | pd const& b) 16 | { 17 | return a.first < b.first; 18 | } 19 | }; 20 | 21 | // return -1 if impossible to defuse, otherwise time it took; 22 | // assume is_defused[j] is false 23 | int defuse(int n, int curr_time, vector ×, int j, vector &is_defused) { 24 | // cerr << j << endl; 25 | int t_j = times[j]; 26 | if(j >= (n - 1) / 2) { // bombs at bottom 27 | if(curr_time < t_j) { 28 | is_defused[j] = true; 29 | return curr_time + 1; 30 | } else { 31 | return -1; 32 | } 33 | } else { 34 | int b1 = 2 * j + 1; 35 | int b2 = 2 * j + 2; 36 | if(!is_defused[b1]) { 37 | curr_time = defuse(n, curr_time, times, b1, is_defused); 38 | } 39 | if(curr_time == -1) { 40 | return -1; 41 | } 42 | if(!is_defused[b2]) { 43 | curr_time = defuse(n, curr_time, times, b2, is_defused); 44 | } 45 | if(curr_time == -1 || curr_time >= t_j) { 46 | return -1; 47 | } else { 48 | is_defused[j] = true; 49 | return curr_time + 1; 50 | } 51 | } 52 | } 53 | 54 | void testcase() { 55 | int n; cin >> n; 56 | vector times(n); 57 | vector q(n); 58 | vector is_defused(n, false); 59 | 60 | for(int i = 0; i < n; i++) { 61 | cin >> times[i]; 62 | q[i] = {times[i], i}; 63 | } 64 | std::sort(q.begin(), q.end(), myComp()); 65 | 66 | int curr_time = 0; 67 | for(int i = 0; i < n; i++) { 68 | int j = q[i].second; 69 | if(!is_defused[j]) { 70 | curr_time = defuse(n, curr_time, times, j, is_defused); 71 | } 72 | if(curr_time == -1) { 73 | cout << "no" << endl; 74 | return; 75 | } 76 | } 77 | cout << "yes" << endl; 78 | 79 | } 80 | 81 | int main() { 82 | std::ios_base::sync_with_stdio(false); // Always! 83 | int t; cin >> t; 84 | while(t--) testcase(); 85 | } -------------------------------------------------------------------------------- /problems/week07-shopping_trip/README.md: -------------------------------------------------------------------------------- 1 | Tags: MaxFlow 2 | 3 | Key ideas: 4 | * possible if there are s edge distinct paths, i.e. flow s -------------------------------------------------------------------------------- /problems/week07-shopping_trip/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // Algolab BGL Tutorial 2 (Max flow, by taubnert@ethz.ch) 2 | // Flow example demonstrating how to use push_relabel_max_flow using a custom edge adder 3 | // to manage the interior graph properties required for flow algorithms 4 | #include 5 | 6 | // BGL include 7 | #include 8 | 9 | // BGL flow include *NEW* 10 | #include 11 | 12 | // Graph Type with nested interior edge properties for flow algorithms 13 | typedef boost::adjacency_list_traits traits; 14 | typedef boost::adjacency_list>>> graph; 18 | 19 | typedef traits::vertex_descriptor vertex_desc; 20 | typedef traits::edge_descriptor edge_desc; 21 | 22 | using namespace std; 23 | 24 | // Custom edge adder class, highly recommended 25 | class edge_adder { 26 | graph &G; 27 | 28 | public: 29 | explicit edge_adder(graph &G) : G(G) {} 30 | 31 | void add_edge(int from, int to, long capacity) { 32 | auto c_map = boost::get(boost::edge_capacity, G); 33 | auto r_map = boost::get(boost::edge_reverse, G); 34 | const auto e = boost::add_edge(from, to, G).first; 35 | const auto rev_e = boost::add_edge(to, from, G).first; 36 | c_map[e] = capacity; 37 | c_map[rev_e] = 0; // reverse edge has no capacity! 38 | r_map[e] = rev_e; 39 | r_map[rev_e] = e; 40 | } 41 | }; 42 | 43 | void make_it_flow() { 44 | int n, m, s; 45 | cin >> n; cin >> m; cin >> s; 46 | graph G(n); 47 | edge_adder adder(G); 48 | 49 | vector stores(s); 50 | for(int i = 0; i < s; i++) { 51 | cin >> stores[i]; 52 | } 53 | 54 | for(int i = 0; i < m; i++) { 55 | int u, v; cin >> u; cin >> v; 56 | // Add some edges using our custom edge adder 57 | adder.add_edge(u, v, 1); // from, to, capacity 58 | adder.add_edge(v, u, 1); // from, to, capacity 59 | } 60 | 61 | // Add special vertices source and sink 62 | const vertex_desc v_source = boost::add_vertex(G); 63 | const vertex_desc v_sink = boost::add_vertex(G); 64 | adder.add_edge(v_source, 0, INT_MAX); 65 | 66 | for(int i = 0; i < s; i++) { 67 | adder.add_edge(stores[i], v_sink, 1); 68 | } 69 | 70 | // Calculate flow from source to sink 71 | // The flow algorithm uses the interior properties (managed in the edge adder) 72 | // - edge_capacity, edge_reverse (read access), 73 | // - edge_residual_capacity (read and write access). 74 | long flow = boost::push_relabel_max_flow(G, v_source, v_sink); 75 | 76 | if(flow == s) { 77 | std::cout << "yes" << "\n"; 78 | } else { 79 | std::cout << "no" << "\n"; 80 | } 81 | 82 | // Retrieve the capacity map and reverse capacity map 83 | const auto c_map = boost::get(boost::edge_capacity, G); 84 | const auto rc_map = boost::get(boost::edge_residual_capacity, G); 85 | 86 | // Iterate over all the edges to print the flow along them 87 | auto edge_iters = boost::edges(G); 88 | for (auto edge_it = edge_iters.first; edge_it != edge_iters.second; ++edge_it) { 89 | const edge_desc edge = *edge_it; 90 | const long flow_through_edge = c_map[edge] - rc_map[edge]; 91 | std::cerr << "edge from " << boost::source(edge, G) << " to " << boost::target(edge, G) 92 | << " runs " << flow_through_edge 93 | << " units of flow (negative for reverse direction). \n"; 94 | } 95 | } 96 | 97 | int main() { 98 | std::ios_base::sync_with_stdio(false); // Always! 99 | int t; cin >> t; 100 | while(t--) make_it_flow(); 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /problems/week08-bistro/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week08-bistro/README.md -------------------------------------------------------------------------------- /problems/week08-bistro/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | ///2 2 | #include 3 | #include 4 | 5 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 6 | typedef CGAL::Delaunay_triangulation_2 Triangulation; 7 | typedef Triangulation::Edge_iterator Edge_iterator; 8 | 9 | using namespace std; 10 | 11 | int main() 12 | { 13 | // read number of points 14 | size_t n; 15 | while(true){ 16 | cin >> n; 17 | if(n == 0) { 18 | return 0; 19 | } 20 | // read points 21 | vector pts; 22 | pts.reserve(n); 23 | for (std::size_t i = 0; i < n; ++i) { 24 | int x, y; 25 | cin >> x >> y; 26 | pts.push_back(K::Point_2(x, y)); 27 | } 28 | // construct triangulation 29 | Triangulation t; 30 | t.insert(pts.begin(), pts.end()); 31 | int m; cin >> m; 32 | // output all edges 33 | for (int j = 0; j < m; j++) { 34 | int x, y; cin >> x >> y; 35 | K::Point_2 q = K::Point_2(x, y); 36 | Triangulation::Vertex_handle v = t.nearest_vertex(q); 37 | K::Point_2 p = v->point(); 38 | cout << long(CGAL::squared_distance(p, q)) << endl; 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /problems/week08-germs/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week08-germs/README.md -------------------------------------------------------------------------------- /problems/week08-germs/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 5 | typedef CGAL::Delaunay_triangulation_2 Triangulation; 6 | typedef Triangulation::Edge_iterator Edge_iterator; 7 | 8 | 9 | using namespace std; 10 | 11 | int main() 12 | { 13 | // read number of points 14 | size_t n; 15 | while(true){ 16 | cin >> n; 17 | if(n == 0) { 18 | return 0; 19 | } 20 | 21 | int left, bottom, right, top; 22 | cin >> left >> bottom >> right >> top; 23 | 24 | // then we build the Delaunay triangulation in one shot, so as to leave the 25 | // choice of an efficient insertion order to the triangulation structure. By 26 | // giving the points paired with the indices, these indices are used to 27 | // initialize the vertex info accordingly. 28 | // This step takes O(n log n) time (for constructing the triangulation). 29 | vector pts; 30 | pts.reserve(n); 31 | for (std::size_t i = 0; i < n; ++i) { 32 | int x, y; 33 | cin >> x >> y; 34 | pts.push_back(K::Point_2(x, y)); 35 | } 36 | // construct triangulation 37 | Triangulation t; 38 | t.insert(pts.begin(), pts.end()); 39 | 40 | vector min_distances; 41 | min_distances.reserve(n); 42 | 43 | for(auto vertex = t.finite_vertices_begin(); vertex != t.finite_vertices_end(); ++vertex){ 44 | double x = vertex->point().x(); 45 | double y = vertex->point().y(); 46 | double die_distance = std::min({std::abs(left-x), std::abs(right-x), std::abs(top-y), std::abs(bottom-y)}); 47 | 48 | double closest_squared_die_distance = numeric_limits::max(); 49 | auto edge_c = t.incident_edges(vertex); 50 | if(edge_c != 0) { 51 | do { 52 | if (!t.is_infinite(edge_c)) { 53 | closest_squared_die_distance = std::min(closest_squared_die_distance, t.segment(edge_c).squared_length()); 54 | } 55 | } while (++edge_c != t.incident_edges(vertex)); 56 | } 57 | min_distances.push_back(std::min(die_distance-0.5, (sqrt(closest_squared_die_distance) - 1)/2)); 58 | } 59 | 60 | const int mid_point = n/2; 61 | std::nth_element(min_distances.begin(), min_distances.begin() + mid_point, min_distances.end()); 62 | 63 | 64 | double first_to_die = sqrt(*std::min_element(std::begin(min_distances), std::begin(min_distances)+mid_point+1)); 65 | double half_dead = sqrt(min_distances[mid_point]); 66 | double last_to_die = sqrt(*std::max_element(std::begin(min_distances)+mid_point, std::end(min_distances))); 67 | 68 | cout << ceil(first_to_die) << " " << ceil(half_dead) << " "<< ceil(last_to_die) << endl; 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /problems/week08-h1n1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week08-h1n1/README.md: -------------------------------------------------------------------------------- 1 | Tags: Delaunay Triangulation, DFS, BFS, Minimal Spanning Tree, Dijkstra (sort of) 2 | 3 | Key ideas: 4 | * For each route request, can consider: 5 | * 1: closest point of infected is too close (sq. dist < d), so "n" 6 | * 2: otherwise: we can move to any edge of the Voronoi diagram of that is adjacent to this face. 7 | * In case of 2, the question then becomes: moving along the edges of the Voronoi diagram, we want to reach the infinite face (i.e. going outside the convex hull). For this to be possible, we need a path from the current face to the infinite face. We have a graph problem with the faces being the nodes and edges encoding adjacency of triangles/faces. 8 | * Moving along one Voronoi edge means passing between two infected points. Hence, for a person to escape, we need that all distances between infected points (that is, the length of the edge of the triangulation) on a path to freedom is >= 4 * d 9 | * As a precomputation, we can compute the _best bottleneck_ to move out for any face f. In complicated words: the maximal minimal distance along any path from f to the infinite face 10 | * Note here that this best bottleneck is upper bounded by the edges of the convex hull! That is, all bottlenecks are smaller or equal to the max. width from the outer faces to the infinite face 11 | * Algorithm 12 | * Construct graph with edge weights being distances 13 | * Use priority queue to propagate the bottlenecks: 14 | * Starting from infinite face, put on priority queue the minimum of (this faces' bottleneck, edge weight) 15 | * queue is giving max. distance 16 | * Then for any query, it's a simple check of the 2 points: if sq.dist < d, then "n", otherwise bottleneck < 4 * d ? "n" : "y" -------------------------------------------------------------------------------- /problems/week08-h1n1/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // CGAL 13 | #include 14 | #include 15 | #include 16 | 17 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 18 | typedef CGAL::Triangulation_vertex_base_2 Vb; 19 | // face handle with index to identify faces 20 | typedef CGAL::Triangulation_face_base_with_info_2 Fb; 21 | typedef CGAL::Triangulation_data_structure_2 Tds; 22 | typedef CGAL::Delaunay_triangulation_2 Triangulation; 23 | 24 | void testcase(size_t n) { 25 | std::vector pts; 26 | pts.reserve(n); 27 | for (std::size_t i = 0; i < n; ++i) { 28 | int x, y; 29 | std::cin >> x >> y; 30 | pts.push_back(K::Point_2(x, y)); 31 | } 32 | // construct triangulation 33 | Triangulation t; 34 | t.insert(pts.begin(), pts.end()); 35 | 36 | 37 | // name all 38 | size_t num_faces = 1; 39 | for (auto f = t.finite_faces_begin(); f != t.finite_faces_end(); ++f) 40 | f->info() = num_faces++; 41 | 42 | 43 | // adjacency list 44 | std::vector>> edges(num_faces); 45 | for (auto f = t.finite_faces_begin(); f != t.finite_faces_end(); ++f) { 46 | int u = f->info(); 47 | 48 | for (int i = 0; i < 3; ++i) { 49 | auto f2 = f->neighbor(i); 50 | int v = t.is_infinite(f2) ? 0 : f2->info(); 51 | auto p1 = f->vertex((i + 1) % 3)->point(); 52 | auto p2 = f->vertex((i + 2) % 3)->point(); 53 | // inputs are < 2^24, so double suffices 54 | double dist = CGAL::squared_distance(p1, p2); 55 | edges[u].push_back({v, dist}); 56 | if (v == 0) { // we do not loop over infinite faces, so add here 57 | edges[v].push_back({u, dist}); 58 | } 59 | } 60 | } 61 | 62 | std::vector bottleneck(num_faces, -1); 63 | 64 | // get the maximal minimal bottleneck for each face, using a priority queue 65 | // --> priority queue per default gives the largest element 66 | // IMPORTANT to use distance as first argument here, bc it;s the first ordered 67 | std::priority_queue> Q; 68 | Q.push({std::numeric_limits::max(), 0}); // infinite face has unlimited bottleneck 69 | 70 | while(!Q.empty()) { 71 | auto p = Q.top(); Q.pop(); 72 | double val = p.first; 73 | size_t u = p.second; 74 | // IMPORTANT to see here that the value will never improve, i.e. visited are done 75 | // --> starting from the infinite face, all others are bottlenecked by 76 | // the convex hull edges, and then the direct neighbors. 77 | // Since we extract the maximum 78 | if(bottleneck[u] != -1) { 79 | continue; 80 | } 81 | bottleneck[u] = val; 82 | 83 | for(auto neighb : edges[u]) { 84 | double dist = neighb.second; 85 | size_t v = neighb.first; 86 | // unvisited, choose min -> works bc priority queue is for max 87 | if(bottleneck[v] == -1) Q.push({std::min(dist, val), v}); 88 | } 89 | } 90 | 91 | // answer queries 92 | size_t m; 93 | std::cin >> m; 94 | for(size_t j = 0; j < m; j++) { 95 | int x, y; 96 | double d; 97 | std::cin >> x >> y >> d; 98 | auto p = K::Point_2(x,y); 99 | double dist = CGAL::squared_distance(p, t.nearest_vertex(p)->point()); 100 | // std::cerr << d << " " << dist << std::endl; 101 | if(d > dist) { 102 | // too close, not possible 103 | std::cout << "n"; 104 | continue; 105 | } 106 | 107 | auto f = t.locate(p); 108 | auto i = t.is_infinite(f) ? 0 : f->info(); 109 | // bottleneck is too small ? 110 | 4 * d > bottleneck[i] ? std::cout << "n" : std::cout << "y"; 111 | } 112 | std::cout << std::endl; 113 | return; 114 | } 115 | 116 | int main() { 117 | std::ios_base::sync_with_stdio(false); 118 | 119 | int n; 120 | std::cin >> n; 121 | while(n != 0) { 122 | testcase(n); 123 | std::cin >> n; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /problems/week08-light_the_stage/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week08-light_the_stage/README.md: -------------------------------------------------------------------------------- 1 | Tags: Binary Search, Delaunay Triangulation, Nearest Neighbor 2 | 3 | Key idea: 4 | * We have the invariant that if a participant is out after lights [0,i], he will also be out after [0,k] for k >= i. 5 | * It doesn't matter what round a participant got out, we only care about the _last_ round that participants survived 6 | * Hence, let's save for each participant the _lowest_ number of rounds that we found where we know for sure that he's out 7 | * Then, search to find the _lowest_ round i s.t. no one survived rounds [0,i]. Do binary search: 8 | * Start with interval [0,n-1]: for interval [l,r] and midpoint mid=(l+r)/2, compute Delaunay triangulation for lights [l,mid] (We need this in order to find nearest light for each participant in log(n) time) 9 | * For each participant, check if he will be out by those lights by checking if the nearest light is < (h+r)^2 away -> if he is out, store mid as his lowest known round to be out 10 | * If no participant survived, then we know the lowest round i is smaller than mid; set r to mid 11 | * else, set l = mid + 1 12 | * At the end, look for the maximum round stored for the participants, and print out those with same number 13 | 14 | Runtime: For each loop iteration, have a runtime of O(nlogn + mlogn) (triangulation + m queries for nearest vertex). The binary search is executed O(logn) times, hence overall we have O(logn * (nlogn + mlogn)). 15 | 16 | Note: Asymptotically, it does not make a difference to compute the triangulation between [0,mid] or [l,mid], but I got a timeout for the former one! -------------------------------------------------------------------------------- /problems/week08-light_the_stage/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Tags: Binary Search, Delaunay Triangulation, Nearest Neighbor 3 | 4 | Key idea: 5 | * We have the invariant that if a participant is out after lights [0,i], he will also be out after [0,k] for k >= i. 6 | * It doesn't matter what round a participant got out, we only care about the _last_ round that participants survived 7 | * Hence, let's save for each participant the _lowest_ number of rounds that we found where we know for sure that he's out 8 | * Then, search to find the _lowest_ round i s.t. no one survived rounds [0,i]. Do binary search: 9 | * Start with interval [0,n-1]: for interval [l,r] and midpoint mid=(l+r)/2, compute Delaunay triangulation for lights [l,mid] (We need this in order to find nearest light for each participant in log(n) time) 10 | * For each participant, check if he will be out by those lights by checking if the nearest light is < (h+r)^2 away -> if he is out, store mid as his lowest known round to be out 11 | * If no participant survived, then we know the lowest round i is smaller than mid; set r to mid 12 | * else, set l = mid + 1 13 | * At the end, look for the maximum round stored for the participants, and print out those with same number 14 | 15 | Note: Asymptotically, it does not make a difference to compute the triangulation between [0,mid] or [l,mid], but I got a timeout for the former one! 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | using namespace std; 31 | 32 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 33 | typedef CGAL::Triangulation_vertex_base_with_info_2 VB; 34 | typedef CGAL::Triangulation_face_base_2 FB; 35 | typedef CGAL::Triangulation_data_structure_2 Tds; 36 | typedef CGAL::Delaunay_triangulation_2 Triangulation; 37 | 38 | void testcase() 39 | { 40 | // read number of points 41 | size_t m, n; 42 | cin >> m >> n; 43 | 44 | typedef pair IPoint; 45 | vector pts; 46 | pts.reserve(n); 47 | for (std::size_t i = 0; i < m; ++i) { 48 | int x, y, r; 49 | cin >> x >> y >> r; 50 | pts.emplace_back(K::Point_2(x, y), r); 51 | } 52 | 53 | size_t h; 54 | cin >> h; 55 | 56 | vector lights; 57 | for(size_t j = 0; j < n; j++) { 58 | int x, y; 59 | cin >> x >> y; 60 | lights.emplace_back(K::Point_2(x, y), j); 61 | } 62 | size_t l = 0, r = n - 1; 63 | std::vector out_after_rounds(m, numeric_limits::max()); 64 | 65 | // binary search 66 | while(l <= r) { 67 | size_t mid = (l + r) / 2; 68 | Triangulation t; 69 | t.insert(lights.begin() + l, lights.begin() + mid + 1); 70 | int pts_left = m; 71 | for(size_t k = 0; k < m; k++) { 72 | if(out_after_rounds[k] < mid) { 73 | pts_left--; 74 | continue; 75 | } 76 | auto p = pts[k].first; 77 | auto r = pts[k].second; 78 | auto v = t.nearest_vertex(p); 79 | K::FT distance = CGAL::squared_distance(p, v->point()); 80 | K::FT hr = h + r; 81 | if(distance < hr * hr) { // not <=, as area has to be positive 82 | out_after_rounds[k] = min(out_after_rounds[k], mid); 83 | pts_left--; 84 | } 85 | } 86 | 87 | if(pts_left == 0) { 88 | r = mid; 89 | if(l == r) break; 90 | } else { 91 | l = mid + 1; 92 | } 93 | } 94 | 95 | size_t last_game = *max_element(out_after_rounds.begin(), out_after_rounds.end()); 96 | for(size_t i = 0; i < m; i++) { 97 | if(out_after_rounds[i] == last_game) { 98 | cout << i << " "; 99 | } 100 | } 101 | cout << endl; 102 | 103 | } 104 | 105 | int main() { 106 | std::ios_base::sync_with_stdio(false); 107 | 108 | int t; 109 | std::cin >> t; 110 | for (int i = 0; i < t; ++i) 111 | testcase(); 112 | } 113 | -------------------------------------------------------------------------------- /problems/week08-light_the_stage/src/algorithm2.cpp: -------------------------------------------------------------------------------- 1 | // Alternative implementation, a bit more efficient by only 2 | // looping through the leftover participants 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 18 | typedef CGAL::Triangulation_vertex_base_with_info_2 VB; 19 | typedef CGAL::Triangulation_face_base_2 FB; 20 | typedef CGAL::Triangulation_data_structure_2 Tds; 21 | typedef CGAL::Delaunay_triangulation_2 Triangulation; 22 | 23 | void testcase() 24 | { 25 | // read number of points 26 | size_t m, n; 27 | cin >> m >> n; 28 | 29 | typedef pair IPoint; 30 | vector> pts; 31 | pts.reserve(n); 32 | for (std::size_t i = 0; i < m; ++i) { 33 | int x, y, r; 34 | cin >> x >> y >> r; 35 | pts.emplace_back(IPoint(K::Point_2(x, y), r), i); 36 | } 37 | 38 | size_t h; 39 | cin >> h; 40 | 41 | vector lights; 42 | for(size_t j = 0; j < n; j++) { 43 | int x, y; 44 | cin >> x >> y; 45 | lights.emplace_back(K::Point_2(x, y), j); 46 | } 47 | 48 | size_t l = 0, r = n - 1; 49 | // binary search 50 | while(l <= r) { 51 | // std::cerr << l << " " << r << "\n"; 52 | size_t mid = (l + r) / 2; 53 | Triangulation t; 54 | t.insert(lights.begin() + l, lights.begin() + mid + 1); 55 | vector> pts_left; 56 | pts_left.reserve(pts.size()); 57 | for(auto pi : pts) { 58 | auto p = pi.first.first; 59 | auto r = pi.first.second; 60 | auto v = t.nearest_vertex(p); 61 | K::FT distance = CGAL::squared_distance(p, v->point()); 62 | K::FT hr = h + r; 63 | if(distance >= hr * hr) { // not >, as area has to be positive 64 | pts_left.push_back(pi); 65 | } 66 | } 67 | 68 | if(pts_left.size() == 0) { 69 | r = mid; 70 | if(r == l) break; 71 | } else { 72 | l = mid + 1; 73 | pts = pts_left; 74 | } 75 | } 76 | 77 | for(auto p : pts) { 78 | cout << p.second << " "; 79 | } 80 | cout << endl; 81 | 82 | } 83 | 84 | int main() { 85 | std::ios_base::sync_with_stdio(false); 86 | 87 | int t; 88 | std::cin >> t; 89 | for (int i = 0; i < t; ++i) 90 | testcase(); 91 | } 92 | -------------------------------------------------------------------------------- /problems/week08-potw-suez/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week08-potw-suez/README.md: -------------------------------------------------------------------------------- 1 | Tags: Linear Programming 2 | 3 | Key ideas: 4 | * classical LP problem 5 | * want to maximize: sum_{a_i} a_i * 2 * (h + w) 6 | * constraints: posters do not overlap 7 | * they either hit horizontally or vertically 8 | * this means: either (ai + aj) * w/2 == abs(xi - xj) or (ai + aj) * h/2 == abs(yi - yj) 9 | * to find what is the case, the answer is max(2 * abs(xi - xj) / w, 2 * abs(yi - yj) / h) 10 | * to see this: consider e.g. two posters on a horizontal line -> they will never hit vertically 11 | * for pairs of new posters: no problem, as we have n <= 30, so O(n * 2) constraints is no problem 12 | * for pairs between new posters and old posters: 13 | * for all the constraints, there will be a tightest one (i.e. the b in the form of Ax<=b) 14 | * so just loop over all old posters (in total no more than 10^4 loop iters) and add constraint for the minimum over the maximums 15 | * otherwise, we would have too many constraints (last test case) 16 | 17 | To keep in mind: 18 | * Input type is CGAL::Gmpq because of rationals 19 | * solution in round function needs to use double, as int doesn't fit (I forgot this first, and it actually gave me a timeout on 2 testcases) -------------------------------------------------------------------------------- /problems/week08-potw-suez/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | /// 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // choose input type (input coefficients must fit) 10 | typedef CGAL::Gmpq IT; 11 | // choose exact type for solver (CGAL::Gmpz or CGAL::Gmpq) 12 | typedef CGAL::Gmpq ET; 13 | 14 | // program and solution types 15 | typedef CGAL::Quadratic_program Program; 16 | typedef CGAL::Quadratic_program_solution Solution; 17 | // points with index 18 | struct Point 19 | { 20 | int x; 21 | int y; 22 | int ind; 23 | }; 24 | 25 | double floor_to_double(const CGAL::Quotient &x) 26 | { 27 | double a = std::floor(CGAL::to_double(x)); 28 | while (a > x) 29 | a -= 1; 30 | while (a + 1 <= x) 31 | a += 1; 32 | return a; 33 | } 34 | 35 | void testcase() 36 | { 37 | 38 | // set the coefficients of A and b 39 | int n, m, h, w; 40 | std::cin >> n >> m >> h >> w; 41 | 42 | Program lp(CGAL::SMALLER, true, 1, false, 0); 43 | std::vector n_pts(n); 44 | for (int i = 0; i < n; i++) 45 | { 46 | int x, y; 47 | std::cin >> x >> y; 48 | n_pts[i] = {x, y, i}; 49 | lp.set_c(i, -2 * (h + w)); 50 | } 51 | std::vector m_pts(m); 52 | for (int i = 0; i < m; i++) 53 | { 54 | int x, y; 55 | std::cin >> x >> y; 56 | m_pts[i] = {x, y, i}; 57 | } 58 | 59 | int n_constr = 0; 60 | 61 | // constraints between new posters 62 | for (int i = 0; i < n; i++) 63 | { 64 | for (int j = i + 1; j < n; j++) 65 | { 66 | int diff_x = std::abs(n_pts[i].x - n_pts[j].x); 67 | int diff_y = std::abs(n_pts[i].y - n_pts[j].y); 68 | // either they would first hit horizontally or vertically 69 | ET b = std::max(ET(2 * diff_x, w), ET(2 * diff_y, h)); 70 | lp.set_a(i, n_constr, 1); 71 | lp.set_a(j, n_constr, 1); 72 | lp.set_b(n_constr, b); 73 | n_constr++; 74 | } 75 | } 76 | 77 | for (int i = 0; i < n; i++) 78 | { 79 | ET min_constr = ET(std::numeric_limits::max()); 80 | for (int j = 0; j < m; j++) 81 | { 82 | int diff_x = std::abs(n_pts[i].x - m_pts[j].x); 83 | int diff_y = std::abs(n_pts[i].y - m_pts[j].y); 84 | // either they would first hit horizontally or vertically 85 | ET b = std::max(ET(2 * diff_x, w) - 1, ET(2 * diff_y, h) - 1); 86 | min_constr = std::min(min_constr, b); 87 | } 88 | lp.set_u(i, true, min_constr); 89 | } 90 | 91 | 92 | Solution s = CGAL::solve_linear_program(lp, ET()); 93 | // std::cerr << s << std::endl; 94 | if (s.is_unbounded()) // just a sanity check 95 | { 96 | std::cout << "unbounded" << std::endl; 97 | } 98 | else 99 | { 100 | auto val = s.objective_value(); 101 | // cerr << s << std::endl; 102 | std::cout << long(-floor_to_double(val)) << std::endl; 103 | } 104 | } 105 | 106 | int main() 107 | { 108 | std::ios_base::sync_with_stdio(false); // Always! 109 | int t; 110 | std::cin >> t; 111 | while (t--) 112 | testcase(); 113 | } 114 | -------------------------------------------------------------------------------- /problems/week09-algocoon_group/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week09-algocoon_group/README.md -------------------------------------------------------------------------------- /problems/week09-algocoon_group/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // ALGOLAB BGL Tutorial 3 2 | // Flow example demonstrating 3 | // - breadth first search (BFS) on the residual graph 4 | 5 | // Compile and run with one of the following: 6 | // g++ -std=c++11 -O2 bgl_residual_bfs.cpp -o bgl_residual_bfs ./bgl_residual_bfs 7 | // g++ -std=c++11 -O2 -I path/to/boost_1_58_0 bgl_residual_bfs.cpp -o bgl_residual_bfs; ./bgl_residual_bfs 8 | 9 | // Includes 10 | // ======== 11 | // STL includes 12 | #include 13 | #include 14 | #include 15 | #include 16 | // BGL includes 17 | #include 18 | #include 19 | #include 20 | 21 | // BGL graph definitions 22 | // ===================== 23 | // Graph Type with nested interior edge properties for Flow Algorithms 24 | typedef boost::adjacency_list_traits traits; 25 | typedef boost::adjacency_list > > > graph; 29 | // Interior Property Maps 30 | typedef traits::vertex_descriptor vertex_desc; 31 | 32 | typedef boost::graph_traits::edge_descriptor edge_desc; 33 | typedef boost::graph_traits::out_edge_iterator out_edge_it; 34 | 35 | // Custom Edge Adder Class, that holds the references 36 | // to the graph, capacity map and reverse edge map 37 | // =================================================== 38 | class edge_adder { 39 | graph &G; 40 | 41 | public: 42 | explicit edge_adder(graph &G) : G(G) {} 43 | 44 | void add_edge(int from, int to, long capacity) { 45 | auto c_map = boost::get(boost::edge_capacity, G); 46 | auto r_map = boost::get(boost::edge_reverse, G); 47 | const edge_desc e = boost::add_edge(from, to, G).first; 48 | const edge_desc rev_e = boost::add_edge(to, from, G).first; 49 | c_map[e] = capacity; 50 | c_map[rev_e] = 0; // reverse edge has no capacity! 51 | r_map[e] = rev_e; 52 | r_map[rev_e] = e; 53 | } 54 | }; 55 | 56 | 57 | // Main 58 | void testcase() { 59 | // build graph 60 | int n, m; 61 | std::cin >> n >> m; 62 | graph G(n); 63 | edge_adder adder(G); 64 | // auto rc_map = boost::get(boost::edge_residual_capacity, G); 65 | // const vertex_desc v_source = boost::add_vertex(G); 66 | // const vertex_desc v_sink = boost::add_vertex(G); 67 | 68 | for(int k = 0; k < m; k++) { 69 | int i, j, c; 70 | std::cin >> i >> j >> c; 71 | adder.add_edge(i, j, c); 72 | } 73 | 74 | // for(int i = 0; i < n; i++) { 75 | // // adder.add_edge(v_source, i, 1); 76 | // adder.add_edge(i, v_sink, std::numeric_limits::max()); 77 | // } 78 | 79 | int flow = std::numeric_limits::max(); 80 | // auto c_map = boost::get(boost::edge_capacity, G); 81 | // auto rc_map = boost::get(boost::edge_residual_capacity, G); 82 | // Find a min cut via maxflow 83 | for(int i = 1; i < n; i++) { 84 | // const edge_desc e = boost::edge(i,v_sink,G).first; 85 | // const edge_desc rev_e = boost::edge(v_sink,i,G).first; 86 | // const edge_desc rev_e = boost::edge(v_sink,i,g).first; 87 | // std::cerr << c_map[e] << " " << c_map[rev_e] << std::endl; 88 | // c_map[e] = 0; 89 | // rc_map[e] = 0; 90 | // c_map[rev_e] = 0; // reverse edge has no capacity! 91 | int min_cut = boost::push_relabel_max_flow(G, i, 0); 92 | flow = std::min(min_cut, flow); 93 | min_cut = boost::push_relabel_max_flow(G, 0, i); 94 | flow = std::min(min_cut, flow); 95 | // c_map[e] = std::numeric_limits::max(); 96 | // std::cerr << i << " " << min_cut << std::endl; 97 | // rc_map[e] = 0; 98 | } 99 | std::cout << flow << "\n"; 100 | // std::cerr << std::endl; 101 | // // Retrieve the capacity map and reverse capacity map 102 | // const auto c_map = boost::get(boost::edge_capacity, G); 103 | // const auto rc_map = boost::get(boost::edge_residual_capacity, G); 104 | 105 | // // Iterate over all the edges to print the flow along them 106 | // auto edge_iters = boost::edges(G); 107 | // for (auto edge_it = edge_iters.first; edge_it != edge_iters.second; ++edge_it) { 108 | // const edge_desc edge = *edge_it; 109 | // const long flow_through_edge = c_map[edge] - rc_map[edge]; 110 | // std::cerr << "edge from " << boost::source(edge, G) << " to " << boost::target(edge, G) 111 | // << " runs " << flow_through_edge 112 | // << " units of flow (negative for reverse direction). \n"; 113 | // } 114 | } 115 | 116 | int main() { 117 | std::ios_base::sync_with_stdio(false); 118 | std::size_t t; 119 | for (std::cin >> t; t > 0; --t) testcase(); 120 | return 0; 121 | } 122 | -------------------------------------------------------------------------------- /problems/week09-canteen/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week09-canteen/README.md -------------------------------------------------------------------------------- /problems/week09-placing_knights/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week09-placing_knights/README.md -------------------------------------------------------------------------------- /problems/week09-potw-kingdom_defence/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week09-potw-kingdom_defence/README.md -------------------------------------------------------------------------------- /problems/week09-potw-kingdom_defence/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // BGL include 4 | #include 5 | 6 | // BGL flow include *NEW* 7 | #include 8 | 9 | // Graph Type with nested interior edge properties for flow algorithms 10 | typedef boost::adjacency_list_traits traits; 11 | typedef boost::adjacency_list>>> graph; 15 | 16 | typedef traits::vertex_descriptor vertex_desc; 17 | typedef traits::edge_descriptor edge_desc; 18 | 19 | using namespace std; 20 | 21 | // Custom edge adder class, highly recommended 22 | class edge_adder { 23 | graph &G; 24 | 25 | public: 26 | explicit edge_adder(graph &G) : G(G) {} 27 | 28 | void add_edge(int from, int to, long capacity) { 29 | auto c_map = boost::get(boost::edge_capacity, G); 30 | auto r_map = boost::get(boost::edge_reverse, G); 31 | const auto e = boost::add_edge(from, to, G).first; 32 | const auto rev_e = boost::add_edge(to, from, G).first; 33 | c_map[e] = capacity; 34 | c_map[rev_e] = 0; // reverse edge has no capacity! 35 | r_map[e] = rev_e; 36 | r_map[rev_e] = e; 37 | } 38 | }; 39 | 40 | void make_it_flow() { 41 | int l, p; 42 | cin >> l; cin >> p; 43 | graph G(l); 44 | edge_adder adder(G); 45 | // Add special vertices source and sink 46 | const vertex_desc v_source = boost::add_vertex(G); 47 | const vertex_desc v_sink = boost::add_vertex(G); 48 | 49 | vector supply(l); 50 | vector demand(l); 51 | int sum_demands = 0; 52 | int sum_supply = 0; 53 | for(int i = 0; i < l; i++) { 54 | cin >> supply[i]; 55 | cin >> demand[i]; 56 | sum_supply += supply[i]; 57 | } 58 | 59 | for(int i = 0; i < p; i++) { 60 | int u, v; cin >> u; cin >> v; 61 | int min_c, max_c; cin >> min_c; cin >> max_c; 62 | demand[u] += min_c; 63 | supply[v] += min_c; 64 | adder.add_edge(u, v, max_c - min_c); // from, to, capacity 65 | } 66 | 67 | for(int i = 0; i < l; i++) { 68 | if(demand[i] > supply[i]) { 69 | sum_demands += demand[i] - supply[i]; 70 | adder.add_edge(i, v_sink, demand[i] - supply[i]); 71 | } 72 | if(supply[i] > demand[i]) { 73 | adder.add_edge(v_source, i, supply[i] - demand[i]); 74 | } 75 | } 76 | 77 | // Calculate flow from source to sink 78 | // The flow algorithm uses the interior properties (managed in the edge adder) 79 | // - edge_capacity, edge_reverse (read access), 80 | // - edge_residual_capacity (read and write access). 81 | long flow = boost::push_relabel_max_flow(G, v_source, v_sink); 82 | 83 | if(flow == sum_demands) { 84 | std::cout << "yes" << "\n"; 85 | } else { 86 | std::cout << "no" << "\n"; 87 | } 88 | 89 | // Retrieve the capacity map and reverse capacity map 90 | // const auto c_map = boost::get(boost::edge_capacity, G); 91 | // const auto rc_map = boost::get(boost::edge_residual_capacity, G); 92 | 93 | // // Iterate over all the edges to print the flow along them 94 | // auto edge_iters = boost::edges(G); 95 | // for (auto edge_it = edge_iters.first; edge_it != edge_iters.second; ++edge_it) { 96 | // const edge_desc edge = *edge_it; 97 | // const long flow_through_edge = c_map[edge] - rc_map[edge]; 98 | // std::cerr << "edge from " << boost::source(edge, G) << " to " << boost::target(edge, G) 99 | // << " runs " << flow_through_edge 100 | // << " units of flow (negative for reverse direction). \n"; 101 | // } 102 | } 103 | 104 | int main() { 105 | std::ios_base::sync_with_stdio(false); // Always! 106 | int t; cin >> t; 107 | while(t--) make_it_flow(); 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /problems/week09-real_estate/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week09-real_estate/README.md -------------------------------------------------------------------------------- /problems/week09-real_estate/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // ALGOLAB BGL Tutorial 3 2 | // Flow example demonstrating 3 | // - breadth first search (BFS) on the residual graph 4 | 5 | // Compile and run with one of the following: 6 | // g++ -std=c++11 -O2 bgl_residual_bfs.cpp -o bgl_residual_bfs ./bgl_residual_bfs 7 | // g++ -std=c++11 -O2 -I path/to/boost_1_58_0 bgl_residual_bfs.cpp -o bgl_residual_bfs; ./bgl_residual_bfs 8 | 9 | // Includes 10 | // ======== 11 | // STL includes 12 | #include 13 | #include 14 | #include 15 | #include 16 | // BGL includes 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | // Graph Type with nested interior edge properties for Cost Flow Algorithms 24 | typedef boost::adjacency_list_traits traits; 25 | typedef boost::adjacency_list > > > > graph; // new! weightmap corresponds to costs 30 | 31 | typedef boost::graph_traits::edge_descriptor edge_desc; 32 | typedef boost::graph_traits::out_edge_iterator out_edge_it; // Iterator 33 | 34 | // Custom edge adder class 35 | class edge_adder { 36 | graph &G; 37 | 38 | public: 39 | explicit edge_adder(graph &G) : G(G) {} 40 | void add_edge(int from, int to, long capacity, long cost) { 41 | auto c_map = boost::get(boost::edge_capacity, G); 42 | auto r_map = boost::get(boost::edge_reverse, G); 43 | auto w_map = boost::get(boost::edge_weight, G); // new! 44 | const edge_desc e = boost::add_edge(from, to, G).first; 45 | const edge_desc rev_e = boost::add_edge(to, from, G).first; 46 | c_map[e] = capacity; 47 | c_map[rev_e] = 0; // reverse edge has no capacity! 48 | r_map[e] = rev_e; 49 | r_map[rev_e] = e; 50 | w_map[e] = cost; // new assign cost 51 | w_map[rev_e] = -cost; // new negative cost 52 | } 53 | }; 54 | 55 | 56 | // Main 57 | void testcase() { 58 | // build graph 59 | int n, m, s; 60 | std::cin >> n >> m >> s; 61 | graph G(n + m + s); 62 | edge_adder adder(G); 63 | // auto rc_map = boost::get(boost::edge_residual_capacity, G); 64 | const int v_source = boost::add_vertex(G); 65 | const int v_sink = boost::add_vertex(G); 66 | 67 | auto c_map = boost::get(boost::edge_capacity, G); 68 | auto rc_map = boost::get(boost::edge_residual_capacity, G); 69 | 70 | for(int i = 0; i < s; i++) { 71 | int l; 72 | std::cin >> l; 73 | adder.add_edge(v_source, i, l, 0); // no cost 74 | } 75 | 76 | for(int i = 0; i < m; i++) { 77 | int si; 78 | std::cin >> si; 79 | adder.add_edge(si - 1, s + i, 1, 0); // no cost 80 | } 81 | 82 | std::vector costs(n * m); 83 | 84 | for(int i = 0; i < n; i++) { 85 | for(int j = 0; j < m; j++) { 86 | int bij; 87 | std::cin >> bij; 88 | costs[i * m + j] = bij; 89 | // adder.add_edge(s + j, s + m + i, 1, -bij); // no cost 90 | } 91 | adder.add_edge(s + m + i, v_sink, 1, 0); // 1 purchase per buyer, no cost 92 | } 93 | 94 | int max_cost = *std::max_element(costs.begin(), costs.end()); 95 | for(int i = 0; i < n; i++) { 96 | for(int j = 0; j < m; j++) { 97 | int bij = costs[i * m + j]; 98 | adder.add_edge(s + j, s + m + i, 1, -bij + max_cost); // no cost 99 | } 100 | } 101 | 102 | // Find a min cut via maxflow 103 | boost::successive_shortest_path_nonnegative_weights(G, v_source, v_sink); 104 | int cost = boost::find_flow_cost(G); 105 | 106 | // Iterate over all edges leaving the source to sum up the flow values. 107 | int s_flow = 0; 108 | out_edge_it e, eend; 109 | for(boost::tie(e, eend) = boost::out_edges(boost::vertex(v_source,G), G); e != eend; ++e) { 110 | // std::cout << "edge from " << boost::source(*e, G) << " to " << boost::target(*e, G) 111 | // << " with capacity " << c_map[*e] << " and residual capacity " << rc_map[*e] << "\n"; 112 | s_flow += c_map[*e] - rc_map[*e]; 113 | } 114 | std::cout << s_flow << " "; // 5 115 | std::cout << -cost + (s_flow * max_cost) << "\n"; 116 | 117 | } 118 | 119 | int main() { 120 | std::ios_base::sync_with_stdio(false); 121 | std::size_t t; 122 | for (std::cin >> t; t > 0; --t) testcase(); 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /problems/week10-asterix_and_the_chariot_race/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week10-asterix_and_the_chariot_race/README.md: -------------------------------------------------------------------------------- 1 | Tags: DFS, Tree, Recursion 2 | 3 | IDEA: 4 | do dfs starting from root node 0 (as we have a tree) 5 | and recursively solve - we always have 3 cases, as explained below 6 | return 3 optimal values: 7 | - 0: opt cost if this node is definitely taken 8 | - 1: opt cost if this node is covered by parent, but might still be taken 9 | - 2: opt cost if this node is _not_ covered by parent, but might still be taken 10 | 11 | then for current node, we have: 12 | - selected opt = cost[v] + sum_covered 13 | - covered opt = min(sum_not_covered, self_selected) 14 | - not covered opt = min(sum_not_covered + best_diff, self_selected) -------------------------------------------------------------------------------- /problems/week10-asterix_and_the_chariot_race/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* Tags: DFS, Tree, Recursion 3 | IDEA: 4 | do dfs starting from root node 0 (as we have a tree) 5 | and recursively solve - we always have 3 cases, as explained below 6 | return 3 optimal values: 7 | - 0: opt cost if this node is definitely taken 8 | - 1: opt cost if this node is covered by parent, but might still be taken 9 | - 2: opt cost if this node is _not_ covered by parent, but might still be taken 10 | --------------- 11 | then for current node, we have: 12 | selected opt = cost[v] + sum_covered 13 | covered opt = min(sum_not_covered, self_selected) 14 | not covered opt = min(sum_not_covered + best_diff, self_selected) 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | using namespace std; 25 | tuple solve(vector &costs, vector> &edges, int v) { 26 | if(edges[v].size() == 0) { 27 | return {costs[v], 0, costs[v]}; 28 | } 29 | 30 | int cost_v = costs[v]; 31 | int best_diff = numeric_limits::max(); 32 | int sum_covered = 0; 33 | int sum_not_covered = 0; 34 | for(int i : edges[v]) { 35 | // std::cerr << "cerr:" << i << "\n"; 36 | auto rec = solve(costs, edges, i); 37 | sum_covered += get<1>(rec); 38 | sum_not_covered += get<2>(rec); 39 | int selected = get<0>(rec); 40 | best_diff = min(best_diff, selected - get<2>(rec)); 41 | } 42 | 43 | // if we select this node, all children are covered 44 | int self_selected = cost_v + sum_covered; 45 | 46 | // the other cases are as described above, but we 47 | // might still select the node __if__ it is cheaper 48 | 49 | // covered: add nothing to others not covered 50 | int self_covered = min(sum_not_covered, self_selected); 51 | 52 | // not covered: need to select one child, take the cheapest 53 | int self_not_covered = min(sum_not_covered + best_diff, self_selected); 54 | return {self_selected, self_covered, self_not_covered}; 55 | } 56 | 57 | // Main 58 | void testcase() { 59 | // build graph 60 | int n; 61 | std::cin >> n; 62 | 63 | vector> edges(n, vector(0)); 64 | 65 | for(int i = 0; i < n - 1; i++) { 66 | int u, v; 67 | std::cin >> u >> v; 68 | edges[u].push_back(v); 69 | } 70 | 71 | vector costs(n); 72 | for(int i = 0; i < n; i++) { 73 | std::cin >> costs[i]; 74 | } 75 | // best solution is the non covered case for the root, as it has no parent 76 | std::cout << get<2>(solve(costs, edges, 0)) << "\n"; 77 | } 78 | 79 | int main() { 80 | std::ios_base::sync_with_stdio(false); 81 | 82 | int t; 83 | std::cin >> t; 84 | for (int i = 0; i < t; ++i) 85 | testcase(); 86 | } 87 | -------------------------------------------------------------------------------- /problems/week10-asterix_in_switzerland/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week10-asterix_in_switzerland/README.md -------------------------------------------------------------------------------- /problems/week10-asterix_in_switzerland/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | ///1 2 | // ALGOLAB BGL Tutorial 3 3 | // Flow example demonstrating 4 | // - breadth first search (BFS) on the residual graph 5 | 6 | // Compile and run with one of the following: 7 | // g++ -std=c++11 -O2 bgl_residual_bfs.cpp -o bgl_residual_bfs ./bgl_residual_bfs 8 | // g++ -std=c++11 -O2 -I path/to/boost_1_58_0 bgl_residual_bfs.cpp -o bgl_residual_bfs; ./bgl_residual_bfs 9 | 10 | // Includes 11 | // ======== 12 | // STL includes 13 | #include 14 | #include 15 | #include 16 | #include 17 | // BGL includes 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | 24 | // BGL graph definitions 25 | // ===================== 26 | // Graph Type with nested interior edge properties for Flow Algorithms 27 | typedef boost::adjacency_list_traits traits; 28 | typedef boost::adjacency_list > > > graph; 32 | // Interior Property Maps 33 | typedef traits::vertex_descriptor vertex_desc; 34 | 35 | typedef boost::graph_traits::edge_descriptor edge_desc; 36 | typedef boost::graph_traits::out_edge_iterator out_edge_it; 37 | 38 | // Custom Edge Adder Class, that holds the references 39 | // to the graph, capacity map and reverse edge map 40 | // =================================================== 41 | class edge_adder { 42 | graph &G; 43 | 44 | public: 45 | explicit edge_adder(graph &G) : G(G) {} 46 | 47 | void add_edge(int from, int to, long capacity) { 48 | auto c_map = boost::get(boost::edge_capacity, G); 49 | auto r_map = boost::get(boost::edge_reverse, G); 50 | const edge_desc e = boost::add_edge(from, to, G).first; 51 | const edge_desc rev_e = boost::add_edge(to, from, G).first; 52 | c_map[e] = capacity; 53 | c_map[rev_e] = 0; // reverse edge has no capacity! 54 | r_map[e] = rev_e; 55 | r_map[rev_e] = e; 56 | } 57 | }; 58 | 59 | 60 | // Main 61 | void testcase() { 62 | // build graph 63 | int n, m; 64 | std::cin >> n >> m; 65 | graph G(n); 66 | edge_adder adder(G); 67 | // auto c_map = boost::get(boost::edge_capacity, G); 68 | // auto rc_map = boost::get(boost::edge_residual_capacity, G); 69 | const vertex_desc v_source = boost::add_vertex(G); 70 | const vertex_desc v_sink = boost::add_vertex(G); 71 | 72 | std::vector b(n); 73 | long sum_b = 0; 74 | for(int i = 0; i < n; i++) { 75 | std::cin >> b[i]; 76 | if(b[i] > 0) { 77 | sum_b += b[i]; 78 | adder.add_edge(v_source, i, b[i]); 79 | } 80 | else { 81 | adder.add_edge(i, v_sink, -b[i]); 82 | } 83 | } 84 | 85 | for(int k = 0; k < m; k++) { 86 | int i, j, c; 87 | std::cin >> i >> j >> c; 88 | adder.add_edge(i, j, c); 89 | } 90 | 91 | long flow = boost::push_relabel_max_flow(G, v_source, v_sink); 92 | // if(sum_dfs > 0) { 93 | if(flow < sum_b) { 94 | std::cout << "yes" << std::endl; 95 | return; 96 | } 97 | std::cout << "no" << std::endl; 98 | // std::cout << flow << " " << sum_b << " " << sum_abs_b << "\n"; 99 | // std::cerr << std::endl; 100 | // // Retrieve the capacity map and reverse capacity map 101 | // const auto c_map = boost::get(boost::edge_capacity, G); 102 | // const auto rc_map = boost::get(boost::edge_residual_capacity, G); 103 | 104 | // // Iterate over all the edges to print the flow along them 105 | // auto edge_iters = boost::edges(G); 106 | // for (auto edge_it = edge_iters.first; edge_it != edge_iters.second; ++edge_it) { 107 | // const edge_desc edge = *edge_it; 108 | // const long flow_through_edge = c_map[edge] - rc_map[edge]; 109 | // std::cerr << "edge from " << boost::source(edge, G) << " to " << boost::target(edge, G) 110 | // << " cmap " << c_map[edge] << " rcmap " << rc_map[edge] << " and " << flow_through_edge 111 | // << " units of flow (negative for reverse direction). \n"; 112 | // } 113 | } 114 | 115 | int main() { 116 | std::ios_base::sync_with_stdio(false); 117 | std::size_t t; 118 | for (std::cin >> t; t > 0; --t) testcase(); 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /problems/week10-evolution/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week10-evolution/README.md -------------------------------------------------------------------------------- /problems/week10-evolution/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | // BGL includes 10 | // #include 11 | 12 | // // BGL graph definitions 13 | // // ===================== 14 | // // Graph Type with nested interior edge properties for Flow Algorithms 15 | // // ===================== 16 | // // Graph Type, OutEdgeList Type, VertexList Type, (un)directedS 17 | // typedef boost::adjacency_list graph; 19 | // typedef boost::graph_traits::vertex_descriptor vertex_desc; // Vertex Descriptor: with vecS vertex list, this is really just an int in the range [0, num_vertices(G)). 20 | // typedef boost::graph_traits::edge_iterator edge_it; // to iterate over all edges 21 | // typedef boost::graph_traits::out_edge_iterator out_edge_it; 22 | // // Custom Edge Adder Class, that holds the references 23 | // // to the graph, capacity map and reverse edge map 24 | // // =================================================== 25 | 26 | std::vector age; 27 | std::vector solution; 28 | std::vector> curr_path; 29 | 30 | struct second_greater { 31 | bool operator() (const std::pair& t0, const std::pair& t1) { 32 | return t0.second > t1.second; 33 | } 34 | }; 35 | 36 | 37 | void solve_for(int idx, std::vector> &children, 38 | std::vector>> &queries) { 39 | 40 | curr_path.push_back({idx, age[idx]}); 41 | std::vector> queries_for_idx = queries[idx]; 42 | for(auto p : queries_for_idx) { 43 | int sol_idx = p.first; 44 | int b = p.second; 45 | auto j = std::lower_bound(curr_path.begin(), curr_path.end(), std::make_pair(0, b), second_greater()); 46 | solution[sol_idx] = j->first; 47 | } 48 | for(const int i : children[idx]) { 49 | solve_for(i, children, queries); 50 | } 51 | 52 | curr_path.pop_back(); 53 | 54 | } 55 | 56 | // Main 57 | void testcase() { 58 | // build graph 59 | int n, q; 60 | std::cin >> n >> q; 61 | 62 | std::unordered_map name_to_ind; 63 | name_to_ind.clear(); 64 | 65 | std::vector> children(n); 66 | 67 | age.clear(); 68 | age.reserve(n); 69 | 70 | solution.clear(); 71 | solution = std::vector(q); 72 | 73 | curr_path.clear(); 74 | 75 | std::vector ind_to_name; 76 | ind_to_name.reserve(n); 77 | 78 | 79 | int root = -1; 80 | int max_age = std::numeric_limits::min(); 81 | 82 | for(int i = 0; i < n; i++) { 83 | std::string s; int a; 84 | std::cin >> s; std::cin >> a; 85 | name_to_ind.insert({s, i}); 86 | ind_to_name.push_back(s); 87 | age.push_back(a); 88 | if(a > max_age) { 89 | root = i; max_age = a; 90 | } 91 | } 92 | 93 | for(int i = 0; i < n - 1; i++) { 94 | std::string s, p; 95 | std::cin >> s; std::cin >> p; 96 | children[name_to_ind.at(p)].push_back(name_to_ind.at(s)); 97 | } 98 | 99 | std::vector>> queries(n); 100 | 101 | for(int k = 0; k < q; k++) { 102 | std::string s; int b; 103 | std::cin >> s; std::cin >> b; 104 | int idx = name_to_ind.at(s); 105 | queries[idx].push_back({k, b}); 106 | } 107 | 108 | 109 | solve_for(root, children, queries); 110 | 111 | for(int k = 0; k < q; k++) { 112 | std::cout << ind_to_name[solution[k]] << " "; 113 | } 114 | std::cout << std::endl; 115 | } 116 | 117 | int main() { 118 | std::ios_base::sync_with_stdio(false); 119 | std::size_t t; 120 | for (std::cin >> t; t > 0; --t) testcase(); 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /problems/week10-potw-goldeneye/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week10-potw-goldeneye/README.md: -------------------------------------------------------------------------------- 1 | Tags: Delaunay Triangulation, Union-Find, Connected Components, Closest Neighbor 2 | 3 | Key idea: 4 | 5 | 1:1 correspondence between points being connected (connected components in EMST) <==> 6 | 7 | we can move between the disks of the points (for a radius >= 2 * minimal distance betw. them) 8 | 9 | 10 | Can copy code from the EMST template, and adapt: 11 | * three UF structures, one for the given radius U_p, 12 | U_a: minimum power needed to execute _all_ missions 13 | U_b: minimum power needed to execute same set of missions as with inital p 14 | * First connect components of U_p with edges <= p 15 | * then see, for each mission, if 4*distance <= p and 16 | components are connected, mission can be executed 17 | * If can be executed: enlarge U_b until it is covered 18 | * In any case: enlarge U_a until it is covered -------------------------------------------------------------------------------- /problems/week10-worldcup/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week10-worldcup/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week10-worldcup/README.md -------------------------------------------------------------------------------- /problems/week10-worldcup/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // example: how to solve a simple explicit LP 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 10 | typedef CGAL::Delaunay_triangulation_2 Triangulation; 11 | typedef Triangulation::Edge_iterator Edge_iterator; 12 | 13 | // choose input type (input coefficients must fit) 14 | typedef long IT; 15 | // choose exact type for solver (CGAL::Gmpz or CGAL::Gmpq) 16 | typedef CGAL::Gmpz ET; 17 | 18 | // program and solution types 19 | typedef CGAL::Quadratic_program Program; 20 | typedef CGAL::Quadratic_program_solution Solution; 21 | 22 | struct warehouse { 23 | long x; 24 | long y; 25 | long s; 26 | long a; 27 | }; 28 | 29 | struct stadium { 30 | long x; 31 | long y; 32 | long d; 33 | long u; 34 | }; 35 | 36 | long floor_to_long(const CGAL::Quotient& x) 37 | { 38 | double a = std::floor(CGAL::to_double(x)); 39 | while (a > x) a -= 1; 40 | while (a+1 <= x) a += 1; 41 | return a; 42 | } 43 | 44 | void testcase() 45 | { 46 | int n, m, c; 47 | std::cin >> n >> m >> c; 48 | 49 | // create an LP with Ax <= b, lower bound 0 and no upper bounds 50 | Program lp(CGAL::SMALLER, true, 0, false, 0); 51 | 52 | std::vector warehouses(n); 53 | std::vector stadiums(m); 54 | 55 | std::vector points; 56 | points.reserve(n+m); 57 | for(int i = 0; i < n; i++) { 58 | long x, y, s, a; 59 | std::cin >> x >> y >> s >> a; 60 | warehouses[i] = {x, y, s, a}; 61 | points.push_back(K::Point_2(x,y)); 62 | for(int j = 0; j < m; j++) { 63 | // set upper constraints for supply, can't sell more in total 64 | // sum_{j} delivery(w_i->stad_j) <= s_i 65 | lp.set_a(i * m + j, i, 1); 66 | lp.set_b(i, s); 67 | } 68 | } 69 | 70 | for(int j = 0; j < m; j++) { 71 | int x, y; long d, u; 72 | std::cin >> x >> y >> d >> u; 73 | stadiums[j] = {x, y, d, u}; 74 | points.push_back(K::Point_2(x,y)); 75 | for(int i = 0; i < n; i++) { 76 | // set upper constraints for alcohol, can't get more in total 77 | // sum_{i} alcohol(w_i->stad_j) <= 100 * u_j 78 | // (factor 100 because pure alcohol is in percent) 79 | long ai = warehouses[i].a; 80 | lp.set_a(i * m + j, n + j, ai); 81 | lp.set_b(n + j, 100 * u); 82 | 83 | // supply == demand 84 | lp.set_a(i * m + j, n + m + j, 1); 85 | lp.set_b(n + m + j, d); 86 | lp.set_a(i * m + j, n + 2 * m + j, -1); 87 | lp.set_b(n + 2 * m + j, -d); 88 | } 89 | } 90 | 91 | std::vector> revs(n, std::vector(m, 0)); 92 | for(int i = 0; i < n; i++) { 93 | for(int j = 0; j < m; j++) { 94 | int rws; 95 | std::cin >> rws; 96 | revs[i][j] = rws; 97 | // for testsets 1/2 98 | // lp.set_c(i * m + j, -rws); 99 | } 100 | } 101 | 102 | 103 | Triangulation t; 104 | t.insert(points.begin(), points.end()); 105 | 106 | std::vector> contours; 107 | contours.reserve(100); // assumptions for last test case has this true 108 | 109 | for(int i = 0; i < c; i++) { 110 | int x, y; long r; 111 | std::cin >> x >> y >> r; 112 | auto p = K::Point_2(x,y); 113 | auto dist = CGAL::squared_distance(p, t.nearest_vertex(p)->point()); 114 | if(dist < r * r) { 115 | contours.push_back({p, r * r}); 116 | } 117 | } 118 | 119 | for(int i = 0; i < n; i++) { 120 | for(int j = 0; j < m; j++) { 121 | auto p_w = K::Point_2(warehouses[i].x, warehouses[i].y); 122 | auto p_s = K::Point_2(stadiums[j].x, stadiums[j].y); 123 | int num_contours = 0; 124 | for(auto cp : contours) { 125 | double dist_w = CGAL::squared_distance(p_w, cp.first); 126 | double dist_s = CGAL::squared_distance(p_s, cp.first); 127 | bool inside_w = (dist_w < cp.second); 128 | bool inside_s = (dist_s < cp.second); 129 | if((!inside_s && inside_w) || (inside_s && !inside_w)) 130 | num_contours++; 131 | } 132 | lp.set_c(i * m + j, -100 * revs[i][j] + num_contours); 133 | } 134 | } 135 | 136 | Solution s = CGAL::solve_linear_program(lp, ET()); 137 | if(s.is_infeasible()) { 138 | std::cout << "RIOT!" << std::endl; 139 | return; 140 | } else { 141 | auto val = s.objective_value(); 142 | std::cout << floor_to_long(-val / 100) << std::endl; 143 | } 144 | } 145 | 146 | int main() 147 | { 148 | std::ios_base::sync_with_stdio(false); 149 | std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(0); 150 | std::size_t t; 151 | for (std::cin >> t; t > 0; --t) 152 | testcase(); 153 | return 0; 154 | } -------------------------------------------------------------------------------- /problems/week10-worldcup/src/old.cpp: -------------------------------------------------------------------------------- 1 | ///3 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | // for triangulation 11 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 12 | typedef CGAL::Delaunay_triangulation_2 Triangulation; 13 | typedef Triangulation::Edge_iterator Edge_iterator; 14 | 15 | // choose input type (input coefficients must fit) 16 | typedef long IT; 17 | // choose exact type for solver (CGAL::Gmpz or CGAL::Gmpq) 18 | typedef CGAL::Gmpz ET; 19 | 20 | // program and solution types 21 | typedef CGAL::Quadratic_program Program; 22 | typedef CGAL::Quadratic_program_solution Solution; 23 | 24 | using namespace std; 25 | 26 | 27 | long floor_to_long(const CGAL::Quotient & x) 28 | { 29 | double a = std::floor(CGAL::to_double(x)); 30 | while (a > x) a -= 1; 31 | while (a+1 <= x) a += 1; 32 | return a; 33 | } 34 | 35 | void testcase() 36 | { 37 | 38 | // set the coefficients of A and b 39 | Program lp; 40 | int n, m, c; 41 | cin >> n; cin >> m; cin >> c; 42 | // create an LP with Ax <= b, lower bound 0 and no upper bounds 43 | lp = Program(CGAL::SMALLER, true, 0, false, 0); 44 | 45 | vector pts; 46 | pts.reserve(n + m); 47 | 48 | vector> warehouse(n); 49 | vector> stadium(m); 50 | 51 | for(int i = 0; i < n; i++) { 52 | long x, y, s, a; 53 | std::cin >> x >> y >> s >> a; 54 | warehouse[i] = {x,y,s,a}; 55 | 56 | pts.push_back(K::Point_2(x,y)); 57 | // set up constraint \sum_s x_w,s <= s_w 58 | for(int j = 0; j < m; j++) { 59 | lp.set_a(i * m + j, i, 1); 60 | lp.set_b(i, s); 61 | } 62 | } 63 | 64 | for(int j = 0; j < m; j++) { 65 | long x, y, d, u; 66 | std::cin >> x >> y >> d >> u; 67 | stadium[j] = {x,y,d,u}; 68 | 69 | pts.push_back(K::Point_2(x,y)); 70 | for(int i = 0; i < n; i++) { 71 | int a_w = std::get<3>(warehouse[i]); 72 | // upper bound alcohol 73 | lp.set_a(i * m + j, n + 3 * j, a_w); 74 | lp.set_b(n + 3 * j, 100 * u); 75 | // supply == demand 76 | lp.set_a(i * m + j, n + 3 * j + 1, -1); 77 | lp.set_b(n + 3 * j + 1, -d); 78 | lp.set_a(i * m + j, n + 3 * j + 2, 1); 79 | lp.set_b(n + 3 * j + 2, d); 80 | } 81 | } 82 | 83 | vector r(n * m); 84 | for(int i = 0; i < n; i++) { 85 | for(int j = 0; j < m; j++) { 86 | int r_ws; std::cin >> r_ws; 87 | r[i * m + j] = r_ws; 88 | } 89 | } 90 | 91 | //////////////////////////////////// 92 | 93 | 94 | Triangulation t; 95 | t.insert(pts.begin(), pts.end()); 96 | 97 | vector> contours; 98 | contours.reserve(100); // according to assumptions that's enough 99 | for(int i = 0; i < c; i++) { 100 | long x, y, r; 101 | std::cin >> x >> y >> r; 102 | K::Point_2 p = K::Point_2(x,y); 103 | auto v = t.nearest_vertex(p); 104 | auto dist = CGAL::squared_distance(v->point(), p); 105 | 106 | if(dist < r * r) { 107 | contours.push_back({x,y,r * r}); 108 | } 109 | } 110 | 111 | for(int i = 0; i < n; i++) { 112 | K::Point_2 wareho = K::Point_2(get<0>(warehouse[i]), get<1>(warehouse[i])); 113 | for(int j = 0; j < m; j++) { 114 | K::Point_2 stad = K::Point_2(get<0>(stadium[j]), get<1>(stadium[j])); 115 | int t_ws = 0; 116 | for(auto cont : contours) { 117 | K::Point_2 cp = K::Point_2(get<0>(cont), get<1>(cont)); 118 | auto d1 = CGAL::squared_distance(wareho, cp) < get<2>(cont); 119 | auto d2 = CGAL::squared_distance(stad, cp) < get<2>(cont); 120 | if((d1 && !d2) || (!d1 && d2)) { // only traverse if only one is inside; we know they do cross only once 121 | t_ws++; 122 | } 123 | } 124 | lp.set_c(i * m + j, -100 * r[i * m + j] + t_ws); 125 | } 126 | } 127 | 128 | 129 | //////////////////////////////////// solve 130 | Solution sol = CGAL::solve_linear_program(lp, ET()); 131 | // cerr << sol << endl; 132 | 133 | if(sol.is_infeasible()) { 134 | cout << "RIOT!" << endl; 135 | return; 136 | } else { 137 | auto val = sol.objective_value(); 138 | cout << floor_to_long(-val / 100) << endl; 139 | } 140 | 141 | } 142 | 143 | int main() { 144 | std::ios_base::sync_with_stdio(false); // Always! 145 | int t; cin >> t; 146 | while(t--) testcase(); 147 | } 148 | -------------------------------------------------------------------------------- /problems/week11-asterix_and_the_legions/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week11-asterix_and_the_legions/README.md: -------------------------------------------------------------------------------- 1 | Tags: Linear Programming, Linear Separation 2 | 3 | Key ideas: 4 | * Linear Program with variables x, y, r 5 | * Objective: maximize r 6 | * Constraints: 7 | * We have initial point p_x, p_y 8 | * For each line with a * x + b * y + c = 0, to stay on the same side: 9 | * if a * p_x + b * p_y + c < 0, we need conversely need a * x + b * y <= -c 10 | * if a * p_x + b * p_y + c > 0, we need conversely need -a * x - b * y <= c 11 | * cf [Inball](../week6-inball) (or the hints given) that we have the distance to the line: 12 | * (a * x + b * y + c / sqrt(a^2+b^2)) 13 | * To also consider the speed v of the legion, we additionally multiply r by v in the constraint 14 | * This gives the actual constraint +-a * x +- b * y + sqrt(a^2+b^2) * v * r <>= +- c 15 | * As usual, solve, read out solution (which is guaranteed to exist here) 16 | * IT int suffices, but the computations with a, b, and the sqrt need double or long -------------------------------------------------------------------------------- /problems/week11-asterix_and_the_legions/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | // choose input type (input coefficients must fit) 14 | typedef long IT; 15 | // choose exact type for solver (CGAL::Gmpz or CGAL::Gmpq) 16 | typedef CGAL::Gmpz ET; 17 | 18 | // program and solution types 19 | typedef CGAL::Quadratic_program Program; 20 | typedef CGAL::Quadratic_program_solution Solution; 21 | 22 | void testcase() { 23 | 24 | long xs, ys, n; 25 | std::cin >> xs >> ys >> n; 26 | // create an LP with Ax <= b and no lower/upper bounds 27 | Program lp (CGAL::SMALLER, false, 0, false, 0); 28 | const int x = 0; 29 | const int y = 1; 30 | const int r = 2; 31 | 32 | for(int i = 0; i < n; i++) { 33 | long a, b, c, v; 34 | std::cin >> a >> b >> c >> v; 35 | long len = std::sqrt(a * a + b * b); 36 | // set up constraint a x + b y + c <= 0 37 | if(a * xs + b * ys + c < 0) { 38 | lp.set_a(x, i, a); 39 | lp.set_a(y, i, b); 40 | lp.set_a(r, i, len * v); 41 | lp.set_b(i, -c); 42 | } else { // flip signs 43 | lp.set_a(x, i, -a); 44 | lp.set_a(y, i, -b); 45 | lp.set_a(r, i, len * v); 46 | lp.set_b(i, c); 47 | } 48 | } 49 | 50 | // enforce that r is positive 51 | lp.set_l(r, true, 0); 52 | // maximize 53 | lp.set_c(r, -1); 54 | // solve the program, using ET as the exact type 55 | Solution s = CGAL::solve_linear_program(lp, ET()); 56 | assert(s.solves_linear_program(lp)); 57 | 58 | // output solution 59 | std::cout << -s.objective_value().numerator() / s.objective_value().denominator() << std::endl; 60 | } 61 | 62 | int main() { 63 | std::ios_base::sync_with_stdio(false); 64 | 65 | int t; 66 | std::cin >> t; 67 | for (int i = 0; i < t; ++i) 68 | testcase(); 69 | } 70 | -------------------------------------------------------------------------------- /problems/week11-idefix/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week11-idefix/README.md: -------------------------------------------------------------------------------- 1 | Tags: Delaunay Triangulation, Union-Find, Connected Components, Closest Neighbor 2 | 3 | Key idea: Close relation to Golden Eye problem: 4 | 1:1 correspondence between points being connected (connected components in EMST) 5 | <==> 6 | we can move between the disks of the points 7 | (for a radius >= 2 * minimal distance betw. them) 8 | 9 | 10 | Can copy code from the EMST template, and adapt: 11 | * two UF structures, one for given initial radius with s, 12 | one for radius needed to get k 13 | * for first problem (max bones): 14 | for each component of tree, store the number of bones reachable 15 | (i.e. 4 *distance to closest point <= s) 16 | This can be done via a simple array, as UF comp. have index <= n 17 | Upon merging, add together the numbers. At the end (when all edges <= s considerd) 18 | look for max 19 | * for second problem (min. radius b to get k): 20 | Have additional edges from each bone to closest point with edge weight 4 * dist. 21 | Sort as usual, then do same procedure as above (i.e. UF) until one comp has >= k bones 22 | Then the latest edge considered has minimal radius needed 23 | 24 | Why 4 * dist? -> 25 | for correct b, need to consider radius over all edges, but for edges between trees 26 | we have that dist = radius / 2. When we sort, we would thus mix up scale -------------------------------------------------------------------------------- /problems/week11-potw-phantom_menace/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haeggee/algolab/176a7d4efbbfb2842f46e93250be00d3b59e0ec3/problems/week11-potw-phantom_menace/README.md -------------------------------------------------------------------------------- /problems/week11-potw-phantom_menace/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // ALGOLAB BGL Tutorial 3 2 | // Flow example demonstrating 3 | // - breadth first search (BFS) on the residual graph 4 | 5 | // Compile and run with one of the following: 6 | // g++ -std=c++11 -O2 bgl_residual_bfs.cpp -o bgl_residual_bfs ./bgl_residual_bfs 7 | // g++ -std=c++11 -O2 -I path/to/boost_1_58_0 bgl_residual_bfs.cpp -o bgl_residual_bfs; ./bgl_residual_bfs 8 | 9 | // Includes 10 | // ======== 11 | // STL includes 12 | #include 13 | #include 14 | #include 15 | #include 16 | // BGL includes 17 | #include 18 | #include 19 | #include 20 | 21 | // BGL graph definitions 22 | // ===================== 23 | // Graph Type with nested interior edge properties for Flow Algorithms 24 | typedef boost::adjacency_list_traits traits; 25 | typedef boost::adjacency_list > > > graph; 29 | // Interior Property Maps 30 | typedef traits::vertex_descriptor vertex_desc; 31 | 32 | typedef boost::graph_traits::edge_descriptor edge_desc; 33 | typedef boost::graph_traits::out_edge_iterator out_edge_it; 34 | 35 | // Custom Edge Adder Class, that holds the references 36 | // to the graph, capacity map and reverse edge map 37 | // =================================================== 38 | class edge_adder { 39 | graph &G; 40 | 41 | public: 42 | explicit edge_adder(graph &G) : G(G) {} 43 | 44 | void add_edge(int from, int to, long capacity) { 45 | auto c_map = boost::get(boost::edge_capacity, G); 46 | auto r_map = boost::get(boost::edge_reverse, G); 47 | const edge_desc e = boost::add_edge(from, to, G).first; 48 | const edge_desc rev_e = boost::add_edge(to, from, G).first; 49 | c_map[e] = capacity; 50 | c_map[rev_e] = 0; // reverse edge has no capacity! 51 | r_map[e] = rev_e; 52 | r_map[rev_e] = e; 53 | } 54 | }; 55 | 56 | 57 | // Main 58 | void testcase() { 59 | // build graph 60 | int n, m, s, d; 61 | std::cin >> n >> m >> s >> d; 62 | graph G(2 * n); 63 | edge_adder adder(G); 64 | // auto rc_map = boost::get(boost::edge_residual_capacity, G); 65 | const vertex_desc v_source = boost::add_vertex(G); 66 | const vertex_desc v_sink = boost::add_vertex(G); 67 | 68 | for(int k = 0; k < m; k++) { 69 | int i, j; 70 | std::cin >> i >> j; 71 | adder.add_edge(2 * i + 1, 2 * j, 1); // from out_i to in_j 72 | } 73 | 74 | for(int k = 0; k < s; k++) { 75 | int i; 76 | std::cin >> i; 77 | adder.add_edge(v_source, 2 * i, 1); // from sink to in_u 78 | } 79 | 80 | for(int k = 0; k < d; k++) { 81 | int i; 82 | std::cin >> i; 83 | adder.add_edge(2 * i + 1, v_sink, 1); // from sink to in_u 84 | } 85 | 86 | for(int i = 0; i < n; i++) 87 | adder.add_edge(2 * i, 2 * i + 1, 1); 88 | 89 | // Find a min cut via maxflow 90 | int flow = boost::push_relabel_max_flow(G, v_source, v_sink); 91 | std::cout << flow << "\n"; 92 | 93 | } 94 | 95 | int main() { 96 | std::ios_base::sync_with_stdio(false); 97 | std::size_t t; 98 | for (std::cin >> t; t > 0; --t) testcase(); 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /problems/week11-return_of_the_jedi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week11-return_of_the_jedi/README.md: -------------------------------------------------------------------------------- 1 | Tags: Minimal Spanning Tree, DFS, 2nd MST 2 | 3 | Key idea: 4 | * we look for 2nd best MST 5 | * construct MST with Kruskal 6 | --> Leias solution (equivalent to her Prim algorithm in terms of weight) 7 | * compute max edge on pairwise paths in MST with DFS in O(n^2) (paths are unique in tree) 8 | * loop through all edges _not_ in MST and 9 | compute best differences in adding edge (u,v) and 10 | removing worst edge on MST path between u and v 11 | * WHY? 2nd best MST is attained by adding a single edge that previosuly wasnt in MST 12 | and then deleting the largest edge in the cycle that is introduced. 13 | * Since the graph is fully connected here, it is more efficient to 14 | precompute pairwise max_edge_weights in O(n^2) than to do it for each 15 | edge in O(E * V) -------------------------------------------------------------------------------- /problems/week11-return_of_the_jedi/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | /* Tags: Minimal Spanning Tree, DFS, 2nd MST 2 | 3 | Key idea: * we look for 2nd best MST 4 | * construct MST with Kruskal 5 | --> Leias solution (equivalent to her Prim algorithm in terms of weight) 6 | * compute max edge on pairwise paths in MST with DFS in O(n^2) (paths are unique in tree) 7 | * loop through all edges _not_ in MST and 8 | compute best differences in adding edge (u,v) and 9 | removing worst edge on MST path between u and v 10 | * WHY? 2nd best MST is attained by adding a single edge that previosuly wasnt in MST 11 | and then deleting the largest edge in the cycle that is introduced. 12 | * Since the graph is fully connected here, it is more efficient to 13 | precompute pairwise max_edge_weights in O(n^2) than to do it for each 14 | edge in O(E * V) 15 | */ 16 | 17 | // STL includes 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | // BGL includes 24 | #include 25 | #include 26 | #include 27 | 28 | typedef std::size_t Index; 29 | 30 | typedef boost::adjacency_list > weighted_graph; 32 | typedef boost::property_map::type weight_map; 33 | typedef boost::graph_traits::edge_descriptor edge_desc; 34 | typedef boost::graph_traits::vertex_descriptor vertex_desc; 35 | // edge: (u,v,c) 36 | typedef std::tuple Edge; 37 | typedef std::vector EdgeV; 38 | 39 | void testcase() 40 | { 41 | int n, tatt; 42 | std::cin >> n >> tatt; 43 | EdgeV edges; 44 | edges.reserve(n*n); 45 | for(int i = 0; i < n - 1; i++) { 46 | for(int j = 0; j < n - i - 1; j++) { 47 | int c; 48 | std::cin >> c; 49 | edges.emplace_back(i, i + j + 1, c); 50 | } 51 | } 52 | 53 | std::sort(edges.begin(), edges.end(), 54 | [](const Edge& e1, const Edge& e2) -> bool { 55 | return std::get<2>(e1) < std::get<2>(e2); 56 | }); 57 | 58 | // construct MST with Kruskal --> Leias solution (equivalent to her Prim algorithm) 59 | std::vector> edge_in_mst(n, std::vector(n, 0)); // nonzero means included 60 | boost::disjoint_sets_with_storage<> uf(n); 61 | int mst_cost = 0; 62 | int n_components = n; 63 | std::vector> mst_edges(n, std::vector(0)); 64 | for (EdgeV::const_iterator e = edges.begin(); e != edges.end(); ++e) { 65 | Index i1 = std::get<0>(*e); 66 | Index i2 = std::get<1>(*e); 67 | Index c1 = uf.find_set(i1); 68 | Index c2 = uf.find_set(i2); 69 | int cost = std::get<2>(*e); 70 | if(c1 != c2) { 71 | mst_cost += cost; 72 | uf.link(c1, c2); 73 | edge_in_mst[i1][i2] = cost; 74 | edge_in_mst[i2][i1] = cost; 75 | mst_edges[i1].push_back(i2); 76 | mst_edges[i2].push_back(i1); 77 | if(--n_components == 1) break; 78 | } 79 | } 80 | 81 | // compute max edge on pairwise paths in MST with DFS 82 | std::vector> max_e_betw(n, std::vector(n, 0)); 83 | for(int i = 0; i < n; i++) { 84 | std::stack> Q; // pair of (node, max edge on path form i to node) 85 | Q.push({i,0}); 86 | std::vector visited(n, false); 87 | while(!Q.empty()) { 88 | auto v = Q.top(); 89 | int j = v.first; int c = v.second; 90 | Q.pop(); 91 | if(!visited[j]) { 92 | visited[j] = true; 93 | max_e_betw[i][j] = c; 94 | for(auto k : mst_edges[j]) Q.push({k, std::max(c, edge_in_mst[j][k])}); 95 | } 96 | } 97 | } 98 | 99 | // loop through all edges not in MST, compute best differences in adding edge (u,v) and 100 | // removing worst edge on MST path between u and v 101 | int min_diff = std::numeric_limits::max(); 102 | for (EdgeV::const_iterator e = edges.begin(); e != edges.end(); ++e) { 103 | Index i1 = std::get<0>(*e); 104 | Index i2 = std::get<1>(*e); 105 | int cost = std::get<2>(*e); 106 | if(edge_in_mst[i1][i2] == 0) { 107 | min_diff = std::min(min_diff, cost - max_e_betw[i1][i2]); 108 | } 109 | } 110 | 111 | std::cout << mst_cost + min_diff << std::endl; 112 | } 113 | 114 | int main() { 115 | std::ios_base::sync_with_stdio(false); 116 | 117 | int t; 118 | std::cin >> t; 119 | for (int i = 0; i < t; ++i) 120 | testcase(); 121 | } 122 | -------------------------------------------------------------------------------- /problems/week11-the_iron_islands/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week11-the_iron_islands/README.md: -------------------------------------------------------------------------------- 1 | Tags: Sliding Window, Prefix Sum, Binary Search, Sorting 2 | 3 | Key ideas: 4 | * For a solution to be valid, the set of islands has to be contiguous. 2 cases for that 5 | * Either it is within a single waterway, or 6 | * It traverses the island 0 (Pyke) and spreads across 2 waterways 7 | * More waterways aren't possible 8 | * The first possibility simply boils down to a sliding window solution that looks for an exact sum match 9 | * For the second possibility, we require prefix sums and binary search: 10 | * First observe: for any solution including the island 0 and going across at least 2 waterways, we have that the interval has value: sum_0^i [0,i'] + sum_0^j [0,j'] - val[0] 11 | * For each island, we can thus precompute the value from island 0 up to itself 12 | * Then, for each island we wanna binary search to find the matching value on a different waterway: 13 | * If we loop over the different pairs of waterways, this becomes too slow (O(n^2)) 14 | * However: we only care about the _longest_ matching other prefix sum 15 | * Hence: Put them all together in a vector and sort i) first after the prefix sum, and ii) second after the length/distance to island 0 16 | * Importantly, the binary search is then only with the comparator of the prefix sum 17 | * Edge case: keep also track of the waterway index when we compare, i.e. to not consider the same waterway. Since the binary search returns the first matching value, we can safely increase the pointer and check if the next key matches -------------------------------------------------------------------------------- /problems/week11-the_iron_islands/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void testcase() { 10 | int n, k, w; 11 | std::cin >> n >> k >> w; 12 | std::vector men(n); 13 | for(int i = 0; i < n; i++) 14 | std::cin >> men[i]; 15 | 16 | std::vector> waterways(w); 17 | std::vector> waterways_prefix(w); 18 | for(int i = 0; i < w; i++) { 19 | int l; std::cin >> l; 20 | waterways[i] = std::vector(l); 21 | waterways_prefix[i] = std::vector(l); 22 | for(int j = 0; j < l; j++) { 23 | int r; std::cin >> r; 24 | waterways[i][j] = men[r]; 25 | waterways_prefix[i][j] = j == 0 ? men[r] : men[r] + waterways_prefix[i][j-1]; 26 | } 27 | } 28 | // first look for solution only inside single waterway via sliding window 29 | int max_interv = 0; 30 | 31 | // runtime O(n), because we visit every city at most twice (apart from city 0) 32 | for(int wi = 0; wi < w; wi++) { 33 | int i = 0, j = 0; 34 | int val = waterways[wi][0]; 35 | int l = waterways[wi].size(); 36 | while (j < l) { 37 | if (val < k) { 38 | j++; 39 | if (j == l) break; 40 | val += waterways[wi][j]; 41 | } else { 42 | if (val == k) { 43 | max_interv = std::max(max_interv, j - i + 1); 44 | } 45 | val -= waterways[wi][i]; 46 | i++; 47 | if (i > j) { 48 | if (i == l) break; 49 | j = i; 50 | val = waterways[wi][i]; 51 | } 52 | } 53 | } 54 | } 55 | 56 | typedef std::tuple Path; // {prefix_sum, dist from 0, waterway i} 57 | std::vector paths; 58 | paths.reserve(n); 59 | for(int i = 0; i < w; i++) { 60 | for(int j = 1; j < waterways[i].size(); j++) { 61 | paths.emplace_back(waterways_prefix[i][j], j, i); 62 | } 63 | } 64 | 65 | std::sort(paths.begin(), paths.end(), 66 | [](const Path& e1, const Path& e2) -> bool { 67 | return std::get<0>(e1) < std::get<0>(e2) 68 | || (std::get<0>(e1) == std::get<0>(e2) && std::get<1>(e1) > std::get<1>(e2)); 69 | }); 70 | 71 | for(auto p : paths) { 72 | // std::cerr << "p: " << std::get<0>(p) << " " << std::get<1>(p) << " " << std::get<2>(p) << std::endl; 73 | int key = k - std::get<0>(p) + men[0]; 74 | // std::cerr << "key: " << key << std::endl; 75 | auto first = paths.begin(); 76 | auto last = paths.end(); 77 | auto el = std::lower_bound(first, last, std::tuple({key, 0, 0}), 78 | [](const Path& e1, const Path& e2) -> bool { 79 | return std::get<0>(e1) < std::get<0>(e2); 80 | // || (std::get<0>(e1) == std::get<0>(e2) && std::get<1>(e1) > std::get<1>(e2)); 81 | }); 82 | // std::cerr << "el: " << std::get<0>(*el) << " " << std::get<1>(*el) << " " << std::get<2>(*el) << std::endl; 83 | if(el != last && std::get<0>(*el) == key) { 84 | if(std::get<2>(*el) != std::get<2>(p)) { 85 | max_interv = std::max(max_interv, (std::get<1>(*el)) + std::get<1>(p) + 1); 86 | } else { 87 | el++; 88 | if(el != last && std::get<0>(*el) == key) { 89 | max_interv = std::max(max_interv, (std::get<1>(*el)) + std::get<1>(p) + 1); 90 | } 91 | } 92 | } 93 | } 94 | std::cout << max_interv << std::endl; 95 | return; 96 | } 97 | 98 | int main() { 99 | std::ios_base::sync_with_stdio(false); 100 | 101 | int t; 102 | std::cin >> t; 103 | for (int i = 0; i < t; ++i) 104 | testcase(); 105 | } 106 | -------------------------------------------------------------------------------- /problems/week12-bonus_level/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week12-bonus_level/README.md: -------------------------------------------------------------------------------- 1 | Tags: MinCost MaxFlow 2 | 3 | Key ideas: 4 | * Important: going from top left to bottom right and back is equivalent to going twice down(imagine just inverting the edges) 5 | * a path down can be seen as one unit of flow 6 | * coins can only be collected once, i.e. we need to consider a capacity limit of one per coin 7 | * In fact: it holds that the maximum can be achieved with two node distinct paths 8 | * to see this: consider a solution where the paths cross at a node. then we can just 'split' the paths in the sense top/bottom of a diagonal; but then, instead of going through the same node twice (and only collecting the coins once), we can take (wlog) the top path and take a detour (collecting at least as much coins) and then consider on the same path 9 | * So, we construct a flow graph: 10 | * connecting nodes down and right, capacity one (node distinct trivially means edge distinct) 11 | * edge node has an in and out node to fulfill single collection constraint 12 | * to have only postive weight: take 100 (the max no. of coins) - a_i,j (to maximize the coins) as the cost between in and out node 13 | 14 | Additionally, I dont use the constraint for the top left and bottom right node: bottom right is only an in node + we need two paths from the top left 15 | 16 | To get correct cost: 17 | * mincost maxflow from out-node top left to bottom right in-node 18 | * every path has exactly 2 * n - 1 positions, subtracting the start and end node gives 2 * n - 3 19 | * hence, we have 4 * n - 6 nodes on two paths from source to sink 20 | * This gives: (4 * n - 6) * 100 - cost1 + a_00 + a_n-1,n-1 21 | 22 | Note that there is also a DP solution possible, but I believe much more complicated and not as intuitive as the flow one. -------------------------------------------------------------------------------- /problems/week12-bonus_level/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | // Includes 9 | // ======== 10 | #include 11 | // BGL includes 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // Graph Type with nested interior edge properties for Cost Flow Algorithms 19 | typedef boost::adjacency_list_traits traits; 20 | typedef boost::adjacency_list > > > > graph; // new! weightmap corresponds to costs 25 | 26 | typedef boost::graph_traits::edge_descriptor edge_desc; 27 | typedef boost::graph_traits::out_edge_iterator out_edge_it; // Iterator 28 | 29 | // Custom edge adder class 30 | class edge_adder { 31 | graph &G; 32 | 33 | public: 34 | explicit edge_adder(graph &G) : G(G) {} 35 | void add_edge(int from, int to, long capacity, long cost) { 36 | auto c_map = boost::get(boost::edge_capacity, G); 37 | auto r_map = boost::get(boost::edge_reverse, G); 38 | auto w_map = boost::get(boost::edge_weight, G); // new! 39 | const edge_desc e = boost::add_edge(from, to, G).first; 40 | const edge_desc rev_e = boost::add_edge(to, from, G).first; 41 | c_map[e] = capacity; 42 | c_map[rev_e] = 0; // reverse edge has no capacity! 43 | r_map[e] = rev_e; 44 | r_map[rev_e] = e; 45 | w_map[e] = cost; // new assign cost 46 | w_map[rev_e] = -cost; // new negative cost 47 | } 48 | }; 49 | 50 | 51 | int ind(int i, int j, int n) { 52 | return i * n + j; 53 | } 54 | 55 | void testcase() { 56 | int n; std::cin >> n; 57 | int nn = n * n; 58 | int sum_a = 0; 59 | std::vector> a(n, std::vector(n)); 60 | for(int i = 0; i < n; i++) { 61 | for(int j = 0; j < n; j++) { 62 | std::cin >> a[i][j]; 63 | sum_a += a[i][j]; 64 | } 65 | } 66 | 67 | graph G(2 * nn); 68 | edge_adder adder(G); 69 | int max_a = 100; 70 | const int v_source = nn; 71 | const int v_sink = nn - 1; 72 | for(int i = 0; i < n; i++) { 73 | for(int j = 0; j < n; j++) { 74 | if(i < n - 1) adder.add_edge(nn + ind(i,j,n), ind(i+1,j,n), 1, 0); 75 | if(j < n - 1) adder.add_edge(nn + ind(i,j,n), ind(i,j+1,n), 1, 0); 76 | if(!( (i == 0 && j == 0) || (i == n - 1 && j == n - 1))) { 77 | adder.add_edge(ind(i,j,n), nn + ind(i,j,n), 1, -a[i][j] + max_a); 78 | } 79 | } 80 | } 81 | 82 | boost::successive_shortest_path_nonnegative_weights(G, v_source, v_sink); 83 | int cost1 = boost::find_flow_cost(G); 84 | // std::cerr << cost1 << " " << (4 * n - 6) * max_a << std::endl; 85 | std::cout << (4 * n - 6) * max_a - cost1 + a[0][0] + a[n-1][n-1] << std::endl; 86 | return; 87 | } 88 | 89 | int main() { 90 | std::ios_base::sync_with_stdio(false); 91 | 92 | int t; 93 | std::cin >> t; 94 | for (int i = 0; i < t; ++i) 95 | testcase(); 96 | } 97 | -------------------------------------------------------------------------------- /problems/week12-car_sharing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week12-car_sharing/README.md: -------------------------------------------------------------------------------- 1 | Tags: MinCost MaxFlow, Maps 2 | 3 | Key ideas: 4 | * Space-time graph, i.e. duplicating the stations across times 5 | * connections between timesteps are either from same station to same station with zero weight, or the requests with the profit as negative weight 6 | * then: can be solved as MinCost MaxFlow problem, connecting the stations at timestep 0 with source with num. cars as capacity 7 | 8 | Challenges here: 9 | * transform negative costs into positive weights by applying the transform: Max_P * (distance in time) - actual_cost, i.e. the prices. same goes for zero edge costs (car staying in a station) -> this will have all s-t paths with the same costs before and after 10 | * do not construct full graph over all timesteps, but only consider timesteps where we actually have requests -> store a map for each station, with keys timesteps and mapping to indices in flow graph -------------------------------------------------------------------------------- /problems/week12-dean_thomas/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week12-dean_thomas/README.md: -------------------------------------------------------------------------------- 1 | Tags: Delaunay Triangulation, DFS/BFS, Priority Queue, Dijkstra, Circle/Radius computation 2 | 3 | Same problem as [Hong Kong](../week12-hong_kong/), just a different story. Used as our test exam. -------------------------------------------------------------------------------- /problems/week12-hong_kong/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week12-hong_kong/README.md: -------------------------------------------------------------------------------- 1 | Tags: Delaunay Triangulation, DFS/BFS, Priority Queue, Dijkstra, Circle/Radius computation 2 | 3 | Key Idea: Same skeleton and concept as [H1N1](../week8-h1n1/). What is different: 4 | * We can also escape inside the convex hull of the points, but for that need a larger distance to all points 5 | * These safe spots can just be boiled down to the centers between Voronoi faces/intersection of Voronoi edges (that's where the distance to points is the largest) 6 | * _However_: Do not need to compute the exact points, but just the radius of the circle induced by the triangles. The radius describes the distance to the 3 points 7 | * As we have the requirement that the **circles** (as geometric objects) of the balloons and trees are r + s_i apart, we essentially have that the center points need to be 2*(r + s_i) apart. 8 | * Hence, we can just initially add the radii of the triangles to the priority queue (no additional multiplicative factor), since the constraint is exactly the same as when moving between faces/along the edges 9 | -------------------------------------------------------------------------------- /problems/week12-majestys_secret_service/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week12-majestys_secret_service/README.md: -------------------------------------------------------------------------------- 1 | Tags: Dijkstra, Shortest Path, Bipartite Graph, Maximum Matching, MaxFlow 2 | 3 | Key ideas: 4 | * Consider case c=1: Every shelter hosts exactly one agent, i.e. we basically have a matching relation between agents <-> shelters 5 | * for a given matching, the bottleneck is the agent that takes the _longest_ to reach a shelter 6 | * **Minimum Bottleneck Matching** 7 | * Can construct a bipartite graph with agents <-> shelters, with edges in between the nodes if an agent can reach a shelter 8 | * No matter what shelter an agent goes to, however, he should take the _shortest_ route to it (minimizing the bottleneck) 9 | * To extract distances: perform Dijkstra for every agent (which gets us the minimum distances to all shelters) 10 | * Then, the task of finding the minimum time needed by searching for the smallest t s.t. matching of size a (number of agents) still possible, i.e. we only add edges with distance(a_i->s_j) + d <= t 11 | * Perform binary search for this! bounded by log(max_dist + d), which is in the order of log(n * max_z) 12 | * To get maximum matching, can use MaxFlow for bipartite graph 13 | 14 | 15 | How to generalize to c=2? 16 | * If an agent reaches a shelter while another one enters (does the protocol), he will have to wait <= d time (equality only in case they both arrive at the same time) 17 | * i.e., if for every matching agent -> 2nd slot in shelter, his time to enter is upper bounded by distance + 2 * d 18 | * Thus: 19 | * Add a copy of the shelters to bipartite graph representing the 2nd slots 20 | * Connect agent with _copy_ of shelters if distance + 2 * d <= t 21 | * Compute matching as usual, see if it has cardinality a 22 | * (Factually, the copies of shelters represent the first slot. Consider: an agent a1 is assigned to a shelter of the first set s1, and another agent a2 is assigned to the same shelter but in s2,and a1 has no edge to s2. Then we know that dist2+2*d <= dist1+d, i.e. dist2+d <= dist1. This then tells us that a1 will wait for a2 to finish, and then we add d to get the bottleneck waiting time) -------------------------------------------------------------------------------- /problems/week12-potw-san_francisco/README.md: -------------------------------------------------------------------------------- 1 | Tags: Dynamic Programming, Graph 2 | 3 | Key Ideas: 4 | * DP table of size [n,k] to represent the max score starting from node i after j moves 5 | * recursion relation: score[i, j+1] = max over edges( score[i, j] + edge from i to v) 6 | * for nodes with no outgoing edges, take the score of node 0 (the source). have to make sure that the loop has filled out the score[0, j+1] first -------------------------------------------------------------------------------- /problems/week12-potw-san_francisco/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | ///3 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | // BGL includes 8 | #include 9 | // BGL graph definitions 10 | // ===================== 11 | typedef boost::adjacency_list > weighted_graph; 13 | typedef boost::property_map::type weight_map; 14 | typedef boost::graph_traits::edge_descriptor edge_desc; 15 | typedef boost::graph_traits::vertex_descriptor vertex_desc; 16 | typedef boost::graph_traits::out_edge_iterator out_edge_it; 17 | 18 | // Main 19 | void testcase() { 20 | // build graph 21 | long n, m, x, k; 22 | std::cin >> n >> m >> x >> k; 23 | weighted_graph G(n); 24 | weight_map weights = boost::get(boost::edge_weight, G); 25 | 26 | edge_desc e; 27 | 28 | std::vector num_outgoing(n, 0); 29 | 30 | for(int i = 0; i < m; i++) { 31 | long u, v, p; 32 | std::cin >> u >> v>> p; 33 | e = boost::add_edge(u, v, G).first; weights[e]=p; 34 | num_outgoing[u] += 1; 35 | } 36 | 37 | std::vector> maxpoints_after(k + 1); 38 | for(int i = 0; i <= k; i++) { 39 | maxpoints_after[i] = std::vector(n, 0); 40 | } 41 | 42 | out_edge_it ebeg, eend; 43 | for(int j = 0; j < k; j++) { 44 | for(int i = 0; i < n; i++) { 45 | long maxpoints = 0; 46 | for (boost::tie(ebeg, eend) = boost::out_edges(i, G); ebeg != eend; ++ebeg) { 47 | const int v = boost::target(*ebeg, G); 48 | const long pi = weights[*ebeg]; 49 | maxpoints = std::max(maxpoints, maxpoints_after[j][v] + pi); 50 | if(i == 0 && maxpoints >= x) { 51 | std::cout << j + 1 << std::endl; 52 | return; 53 | } 54 | } 55 | if(num_outgoing[i] == 0) { 56 | maxpoints_after[j + 1][i] = maxpoints_after[j + 1][0]; 57 | } else { 58 | maxpoints_after[j + 1][i] = maxpoints; 59 | } 60 | } 61 | } 62 | std::cout << "Impossible" << std::endl; 63 | } 64 | 65 | int main() { 66 | std::ios_base::sync_with_stdio(false); 67 | std::size_t t; 68 | for (std::cin >> t; t > 0; --t) testcase(); 69 | return 0; 70 | } -------------------------------------------------------------------------------- /problems/week13-hagrid/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week13-hagrid/README.md: -------------------------------------------------------------------------------- 1 | Tags: Recursion, Tree, Greedy 2 | 3 | Key ideas: 4 | * We have a tree structure 5 | * can do recursive traversal to get best result 6 | * Main challenge: finding best solution in subquadratic time at each point in recursion 7 | 8 | 9 | Solution: 10 | * recurse with three values: 11 | 1. max number of gold one can collect in the subtree (neglecting any kind of offset at the beginning, i.e. only take into account traversal time inside subtree) 12 | 2. number of nodes in subtree 13 | 3. full traversal time 14 | * we can recursively solve, for each recursive solution add 2*l to traversal time (going to and back subtree/edge) and subtract the gold we loose: (#nodes in subtree * l) 15 | * sort the results: r1.traversal_time * r2.num_nodes < r2.traversal_time * r1.num_nodes 16 | * i.e. greedily take the subtree where we loose the least number of gold! 17 | * why is this correct? 18 | * we have the assumption that no matter what traversal, there will always be a bit of gold 19 | * i.e. we always collect nonzero gold! 20 | * effectively, what we get is then #sum of all gold - #sum of gold lost 21 | * --> try to minimize the right term 22 | * For the first few testcases, it suffices to sort after the traversal time -------------------------------------------------------------------------------- /problems/week13-hagrid/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct edge 10 | { 11 | int v; 12 | int l; 13 | }; 14 | 15 | struct result 16 | { 17 | long coins; 18 | long num_nodes; 19 | long traversal_time; // time it takes to go from u to all nodes under v and come back 20 | }; 21 | 22 | std::vector dp; 23 | 24 | typedef std::vector> 25 | Edges; 26 | 27 | result solve(int i, std::vector &galleon, Edges &edges) 28 | { 29 | if (edges[i].size() == 0) 30 | { // leaf 31 | // dp[i] = {galleon[i], 1, 0}; 32 | return {galleon[i], 1, 0}; 33 | } 34 | 35 | std::vector rec_results; 36 | rec_results.reserve(edges[i].size()); 37 | for (auto e : edges[i]) 38 | { 39 | result res = solve(e.v, galleon, edges); 40 | // loose l coins for each node 41 | rec_results.push_back({res.coins - res.num_nodes * e.l, res.num_nodes, res.traversal_time + 2 * e.l}); 42 | } 43 | 44 | std::sort(rec_results.begin(), rec_results.end(), [](const result &r1, const result &r2) -> bool 45 | { return r1.traversal_time * r2.num_nodes < r2.traversal_time * r1.num_nodes; }); 46 | 47 | long total_coins = galleon[i]; 48 | long num_nodes = 0; 49 | long traversal_time = 0; 50 | for(auto r : rec_results) { 51 | // std::cout << i << " " << r.coins << " " << r.num_nodes << " " << " " << r.traversal_time << std::endl; 52 | total_coins += r.coins - traversal_time * r.num_nodes; 53 | num_nodes += r.num_nodes; 54 | traversal_time += r.traversal_time; 55 | } 56 | // std::cout << "result: " << i << " " << total_coins << " " << num_nodes << " " << " " << traversal_time << std::endl; 57 | return {total_coins, num_nodes + 1, traversal_time} ; // return max_reward without taking current time into account 58 | } 59 | 60 | void testcase() 61 | { 62 | int n; 63 | std::cin >> n; 64 | std::vector galleon(n + 1); 65 | Edges edges(n + 1); 66 | dp = std::vector(n + 1); 67 | for (int i = 0; i < n; i++) 68 | { 69 | std::cin >> galleon[i + 1]; 70 | } 71 | 72 | for (int i = 0; i < n; i++) 73 | { 74 | int u, v, l; 75 | std::cin >> u >> v >> l; 76 | edges[u].push_back({v, l}); 77 | } 78 | 79 | std::cout << solve(0, galleon, edges).coins << std::endl; 80 | return; 81 | } 82 | 83 | int main() 84 | { 85 | std::ios_base::sync_with_stdio(false); 86 | 87 | int t; 88 | std::cin >> t; 89 | for (int i = 0; i < t; ++i) 90 | testcase(); 91 | } 92 | -------------------------------------------------------------------------------- /problems/week13-hand/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week13-hand/README.md: -------------------------------------------------------------------------------- 1 | Tags: Delaunay Triangulation, Union Find, Connected Components 2 | 3 | Key ideas: 4 | * In my opinion ressemblance to [Idefix](../week11-idefix/), [GoldenEye](../week10-potw-goldeneye/), [Clues](../week13-potw-clues/) just because we rely on the Delaunay Triangulation + Kruskal and Union Find to compute connected components 5 | * For a given squared distance s, one connected component must be assigned to the same family 6 | 7 | Hence, general approach: get the delaunay edges, sort them, and while iterating through them (up to a certain point) connect components. 8 | 9 | 10 | * For task 1: maximal s for which assigning the tents to families f0 is possible s.t. gets >= k tents 11 | * Iterate through edges and print out the last distance of the last edge added as soon as we create a forest of components that is infeasible with f families 12 | * for case k=1, this is easy: when n_components < f 13 | * for generic case, see below 14 | * For task 2: maximal f s.t. all fams have >= k tents and all distances to diff fams >= s 15 | * Again, iterate through edges and merge components for all edges < s 16 | * for case k=1 the result is then easy: f is the number of components left (each family gets one) 17 | 18 | Generic case k: 19 | * need to optimally assign components to families 20 | * honestly, it's quite a pain to explain in words, e.g.: 21 | * for k = 3: components of >= 3 + (match 2comp with 1 comp) + remaining2 / 2 + remaining1 / 3 -------------------------------------------------------------------------------- /problems/week13-ludo_bagman/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week13-ludo_bagman/README.md: -------------------------------------------------------------------------------- 1 | Tags: MinCost MaxFlow, Bipartite Graph 2 | 3 | Key ideas: 4 | * Bipartite graph, edges in between are potential matches 5 | * to fulfill at least l matches for each team: 6 | * edge from source to sink have two paths: one for the l matches for each, one for the rest 7 | * top path is bottlenecked at e * l, each incoming edge to team has capacity l --> want a full flow there 8 | * same goes for sink 9 | * forcing the "rest" path to have at max p-e*l matches, we have at least l matches 10 | for each 11 | * difficult matches are just a copy of the e+w graph, being connected to the p-e*l bottleneck -------------------------------------------------------------------------------- /problems/week13-potw-clues/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week13-potw-clues/README.md: -------------------------------------------------------------------------------- 1 | Tags: Delaunay Triangulation, Bipartite Graph, Minimal Spanning Tree, Connected Components 2 | 3 | Key ideas: 4 | --- 5 | The problem boils down to 2 things: i) checking if an (implicit) graph is bipartite and ii) if the queried points are connected via the network of points. I choose to make use of BGL instead of implementing BFS and other things from scratch. 6 | 7 | 1. To me, the connection of query points is a simpler problem. 8 | * We know that points with distance <= r are connected. In close relation to [GoldenEye](../week10-potw-goldeneye/) and [Idefix](../week11-idefix/) (where we used Union Find) we thus look at the EMST with edges <= r and the connected components (I use BGL, as might become clear below when getting the coloring too). Assuming the graph of points is bipartite (e.g. testcases 2 and 3), we differ: 9 | * dist(a,b) <= r : simply output yes 10 | * the closest points of the network/triangulation are too far away for either one a or b: output no 11 | * otherwise, we check if the two closest points in the network are in the same component 12 | 2. Bipartite: 13 | * Important note: for the input size, the runtime cannot be O(n^2) - this also tells us that we cannot explicitly create the graph representing connected points, because in a worst case it is fully connected 14 | * Somehow obvious and already from the connection problem above, we compute a Delaunay triangulation of the network points 15 | * The forest of trees given by the Delaunay triangulation already has to be bipartite! Hence: 16 | * Check bipartiteness of the graph given by the Delaunay edges <= r and get the coloring white/black if true 17 | * Then: we can use the coloring found by BGL to split the points into two sets and create one triangulation for each color. Since the Delaunay triangulation again includes the closest neighbor for each point, we have that all edges must be larger than r! I.e. looping through all edges and breaking the loop for a violation suffices 18 | * To see why this guarantees and if and only if relation to the points being bipartite: The triangulation + EMST is guaranteed to find the connected components (since nearest neighbors <= r edges are added). For a connected component in a bipartite graph, _removing_ edges while staying connected cannot change the color assignments. But when we add edges (as in the case of the two additional triangulations), we can detect violations of bipartiteness 19 | 20 | -------------------------------------------------------------------------------- /problems/week13-punch/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week13-punch/README.md: -------------------------------------------------------------------------------- 1 | Tags: Dynamic Programming 2 | 3 | Key ideas: 4 | * Directly ressembles DP task with different beverages, being able to take them or not 5 | * Difference to other recursion/DP tasks that are similar: we can take a beverage multiple times 6 | * Summarized: 7 | * DP table of size n * k that describes the minimum cost 8 | * additionally, return the max. distinct beverages for a min cost 9 | * recursion: for dp[i][k], either: 10 | * do not take beverage i, i.e. dp[i-1][k] 11 | * or take beverage i **at least** once, i.e. cost[i] + d[i-1][k-vol[i] 12 | * additionally, to keep track of _distinct_ beverages, return if beverage i was actually taken in solution dp[i][k] 13 | * then, to break ties, take beverage if: 14 | * take if strict minimal cost (c' < c) 15 | * if same cost, take if dist. beverages are strictly higher 16 | * do not take if same cost, same beverages (this could lead to more distinct beverages downstream/in a higher recursive call) -------------------------------------------------------------------------------- /problems/week13-punch/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int n, k; 10 | struct sol { 11 | int c; 12 | int bev; 13 | bool use_i; 14 | }; 15 | 16 | std::vector> dp; 17 | 18 | sol solve(int i, int k, std::vector &cost, std::vector &vol) { 19 | if(k <= 0) { 20 | return {0, 0, false}; 21 | } 22 | if(i == 0) { 23 | int num = k / vol[i]; 24 | num = k % vol[i] == 0 ? num : num + 1; 25 | // std::cerr << "k " << k << " i " << i << " vol " << vol[i] << " num " << num << std::endl; 26 | return {num * cost[i], 1, true}; 27 | } 28 | if(dp[i][k].c != -1) { 29 | return dp[i][k]; 30 | } 31 | 32 | // two options: take beverage _at least_ once or not at all 33 | 34 | // 1: don't take 35 | sol s1 = solve(i - 1, k, cost, vol); 36 | s1.use_i = false; 37 | // 2: take multiple times 38 | sol s2 = solve(i, k - vol[i], cost, vol); 39 | s2.c += cost[i]; 40 | sol best = s1; 41 | // order: 42 | // 1: take minimal cost 43 | // 2: for same cost: more beverages 44 | // 3: for same cost and same beverages: 45 | // prefer if sol did not take beverage, i.e. we add one and are strictly higher 46 | // -- Q: why does s2.c < best.c || (s2.c == best.c && s2.bev >= best.bev) not give same answer? 47 | // this means that we take beverage even if 48 | if(s2.c < best.c || (s2.c == best.c && s2.bev > best.bev) 49 | || (s2.c == best.c && s2.bev == best.bev && !s2.use_i)) { 50 | best = {s2.c, s2.use_i ? s2.bev : s2.bev + 1, true}; 51 | } 52 | 53 | dp[i][k] = best; 54 | return best; 55 | } 56 | 57 | void testcase() { 58 | std::cin >> n >> k; 59 | std::vector cost(n); 60 | std::vector vol(n); 61 | dp = std::vector>(n, std::vector(k+1, {-1,0,false})); 62 | for(int i = 0; i < n; i++) { 63 | std::cin >> cost[i] >> vol[i]; 64 | } 65 | 66 | sol s = solve(n-1, k, cost, vol); 67 | std::cout << s.c << " " << s.bev << std::endl; 68 | return; 69 | } 70 | 71 | int main() { 72 | std::ios_base::sync_with_stdio(false); 73 | 74 | int t; 75 | std::cin >> t; 76 | for (int i = 0; i < t; ++i) 77 | testcase(); 78 | } 79 | -------------------------------------------------------------------------------- /problems/week14-potw-india/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(algo) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") 5 | 6 | find_package(CGAL REQUIRED COMPONENTS Core) 7 | find_package(Boost REQUIRED) 8 | include_directories(${CGAL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) 9 | include( ${CGAL_USE_FILE} ) 10 | 11 | add_executable(${PROJECT_NAME} "src/algorithm.cpp") 12 | target_link_libraries(${PROJECT_NAME} ${CGAL_LIBRARIES} ${Boost_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${THREAD_LIBRARIES}) 13 | -------------------------------------------------------------------------------- /problems/week14-potw-india/README.md: -------------------------------------------------------------------------------- 1 | Tags: MinCost MaxFlow, Binary search 2 | 3 | Key ideas: 4 | * Model a flow graph with nodes of cities + guides 5 | * For a guide, connect city x-> guide and guide->y. Both edges have capacity e and one of them has cost d, the other zero 6 | * The max flow is limited by the sum of elephants, i.e. we add a source node and connect it with the node k (the initial city) with capacity sum_e 7 | * Then, do binary search to find largest flow s.t. cost <= b: 8 | * start with bounds [0, initial_max_flow] (computed first for capacity sum_e) 9 | * in each iteration, change capacity of source->k edge to mid=(l+r) / 2 10 | * see if cost <= b 11 | * right indices are a bit tricky to get correct IMO: 12 | ``` 13 | while(l<=r) 14 | cost = mincost_maxflow(s->a) 15 | if(cost <= b) { // cost is okay, i.e. go higher 16 | l = mid + 1; 17 | } else { 18 | r = mid; 19 | if(l == r) break; 20 | } 21 | // if we exit loop, l is just one above what is feasible 22 | ``` -------------------------------------------------------------------------------- /problems/week14-potw-india/src/algorithm.cpp: -------------------------------------------------------------------------------- 1 | // Includes 2 | // ======== 3 | #include 4 | // BGL includes 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Graph Type with nested interior edge properties for Cost Flow Algorithms 12 | typedef boost::adjacency_list_traits traits; 13 | typedef boost::adjacency_list > > > > graph; // new! weightmap corresponds to costs 18 | 19 | typedef boost::graph_traits::edge_descriptor edge_desc; 20 | typedef boost::graph_traits::out_edge_iterator out_edge_it; // Iterator 21 | 22 | // Custom edge adder class 23 | class edge_adder { 24 | graph &G; 25 | 26 | public: 27 | explicit edge_adder(graph &G) : G(G) {} 28 | void add_edge(int from, int to, long capacity, long cost) { 29 | auto c_map = boost::get(boost::edge_capacity, G); 30 | auto r_map = boost::get(boost::edge_reverse, G); 31 | auto w_map = boost::get(boost::edge_weight, G); // new! 32 | const edge_desc e = boost::add_edge(from, to, G).first; 33 | const edge_desc rev_e = boost::add_edge(to, from, G).first; 34 | c_map[e] = capacity; 35 | c_map[rev_e] = 0; // reverse edge has no capacity! 36 | r_map[e] = rev_e; 37 | r_map[rev_e] = e; 38 | w_map[e] = cost; // new assign cost 39 | w_map[rev_e] = -cost; // new negative cost 40 | } 41 | }; 42 | 43 | 44 | void testcase() { 45 | 46 | int c, g, b, k, a; 47 | std::cin >> c >> g >> b >> k >> a; 48 | // Create graph, edge adder class and propery maps 49 | graph G(c + g); 50 | edge_adder adder(G); 51 | auto c_map = boost::get(boost::edge_capacity, G); 52 | auto r_map = boost::get(boost::edge_reverse, G); 53 | auto rc_map = boost::get(boost::edge_residual_capacity, G); 54 | 55 | const int v_source = boost::add_vertex(G); 56 | 57 | int sum_elephants = 0; 58 | for(int i = 0; i < g; i++) { 59 | int x, y, d, e; 60 | std::cin >> x >> y >> d >> e; 61 | sum_elephants += e; 62 | adder.add_edge(x, c + i, e, d); 63 | adder.add_edge(c + i, y, e, 0); 64 | } 65 | 66 | // source 67 | adder.add_edge(v_source, k, sum_elephants, 0); 68 | 69 | int l = 0; 70 | int s_flow = 0; 71 | out_edge_it e, eend; 72 | boost::successive_shortest_path_nonnegative_weights(G, v_source, a); 73 | int cost = boost::find_flow_cost(G); 74 | 75 | for(boost::tie(e, eend) = boost::out_edges(boost::vertex(v_source,G), G); e != eend; ++e) { 76 | s_flow += c_map[*e] - rc_map[*e]; 77 | } 78 | 79 | // this is a simple heuristic: we save the binary search 80 | // if cost is not the bottleneck anyway 81 | if(cost <= b) { 82 | std::cout << s_flow << std::endl; 83 | return; 84 | } 85 | 86 | int r = s_flow; 87 | while(l <= r) { 88 | int mid = (l + r) / 2; 89 | // change capacity 90 | const edge_desc e_s = boost::edge(v_source, k, G).first; 91 | const edge_desc rev_e = r_map[e_s]; 92 | c_map[e_s] = mid; 93 | c_map[rev_e] = 0; // reverse edge has no capacity! 94 | boost::successive_shortest_path_nonnegative_weights(G, v_source, a); 95 | int cost = boost::find_flow_cost(G); 96 | out_edge_it e, eend; 97 | if(cost <= b) { // cost is okay, i.e. go higher 98 | l = mid + 1; 99 | } else { 100 | r = mid; 101 | if(l == r) break; 102 | } 103 | } 104 | std::cout << l - 1 << std::endl; 105 | } 106 | int main() { 107 | std::ios_base::sync_with_stdio(false); 108 | 109 | int t; 110 | std::cin >> t; 111 | for (int i = 0; i < t; ++i) 112 | testcase(); 113 | } 114 | --------------------------------------------------------------------------------