├── README.assets ├── defline.png ├── image-20201029143254630.png ├── image-20201029143621938.png ├── image-20201029144113354.png ├── image-20201029144338585.png ├── image-20201029151434018.png ├── image-20201029153914441.png ├── image-20201117155357857.png ├── image-20201207183451370.png ├── image-20201225143237122.png ├── image-20210106101318755.png ├── image-20210106133741713.png ├── image-20210107155006785.png ├── image-20210107155045253.png ├── image-20210108111915037.png ├── image-20210108112354639.png ├── image-20210108112400538.png ├── image-20210108113111997.png ├── image-20210108113252520.png ├── image-20210108113435616.png ├── image-20210110083909495.png ├── image-20210110105029753.png ├── image-20210121093107542.png ├── image-20210121094034950.png ├── image-20210121210926663.png ├── image-20210130155508086.png └── image-20201029143621938-1613493413845.png └── README.md /README.assets/defline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/defline.png -------------------------------------------------------------------------------- /README.assets/image-20201029143254630.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20201029143254630.png -------------------------------------------------------------------------------- /README.assets/image-20201029143621938.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20201029143621938.png -------------------------------------------------------------------------------- /README.assets/image-20201029144113354.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20201029144113354.png -------------------------------------------------------------------------------- /README.assets/image-20201029144338585.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20201029144338585.png -------------------------------------------------------------------------------- /README.assets/image-20201029151434018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20201029151434018.png -------------------------------------------------------------------------------- /README.assets/image-20201029153914441.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20201029153914441.png -------------------------------------------------------------------------------- /README.assets/image-20201117155357857.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20201117155357857.png -------------------------------------------------------------------------------- /README.assets/image-20201207183451370.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20201207183451370.png -------------------------------------------------------------------------------- /README.assets/image-20201225143237122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20201225143237122.png -------------------------------------------------------------------------------- /README.assets/image-20210106101318755.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210106101318755.png -------------------------------------------------------------------------------- /README.assets/image-20210106133741713.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210106133741713.png -------------------------------------------------------------------------------- /README.assets/image-20210107155006785.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210107155006785.png -------------------------------------------------------------------------------- /README.assets/image-20210107155045253.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210107155045253.png -------------------------------------------------------------------------------- /README.assets/image-20210108111915037.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210108111915037.png -------------------------------------------------------------------------------- /README.assets/image-20210108112354639.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210108112354639.png -------------------------------------------------------------------------------- /README.assets/image-20210108112400538.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210108112400538.png -------------------------------------------------------------------------------- /README.assets/image-20210108113111997.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210108113111997.png -------------------------------------------------------------------------------- /README.assets/image-20210108113252520.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210108113252520.png -------------------------------------------------------------------------------- /README.assets/image-20210108113435616.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210108113435616.png -------------------------------------------------------------------------------- /README.assets/image-20210110083909495.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210110083909495.png -------------------------------------------------------------------------------- /README.assets/image-20210110105029753.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210110105029753.png -------------------------------------------------------------------------------- /README.assets/image-20210121093107542.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210121093107542.png -------------------------------------------------------------------------------- /README.assets/image-20210121094034950.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210121094034950.png -------------------------------------------------------------------------------- /README.assets/image-20210121210926663.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210121210926663.png -------------------------------------------------------------------------------- /README.assets/image-20210130155508086.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20210130155508086.png -------------------------------------------------------------------------------- /README.assets/image-20201029143621938-1613493413845.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucidBrot/algolab-tutorial/HEAD/README.assets/image-20201029143621938-1613493413845.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What? 2 | 3 | * Advanced Intro to boost graph library 4 | * Intro to CGAL 5 | * Useful c++ stuff 6 | * Debugging on Code Expert 7 | * Common Pitfalls and Further Things You Should Know 8 | * Ideas behind Exercise Solutions 9 | * Github Repos I recommend 10 | 11 | You might want to download this file and view it in Typora, so that you have a nice table of contents in the sidebar. Also, the math would display well then. 12 | 13 | ## Why? 14 | 15 | The Algolab Lecture at ETH aims to teach you how to put some algorithmic building blocks together and to deal with intentionally confusing exercise statements. 16 | The exercises they provide are great, but the guidance less so. 17 | 18 | I have especially found the tutorial sessions on what the libraries we are supposed to use offer and how to use them quite lacking. Since I haven't found many good comprehensive resources for boost beginners out there, I have kept track of my own progress. 19 | 20 | > "You don't need to understand that, you can copy paste it from the provided example codes during the exam." 21 | 22 | ## How? 23 | 24 | The intended audience are master students taking the Algorithms Lab course. I am assuming generic coding skills similar to the ones I had at the start of the semester. 25 | 26 | I originally planned to go through all my notes again and write them up in better words and more detail, but I think I won't be doing that. See the file `codingtricks.md` for my raw notes that cover more than just the basics in the sections below. 27 | 28 | I think everything I wrote here is valuable, but there are some parts that go deeper into detail than you might care for now. Just skim them and then read it when you need it :) 29 | 30 | # How To Boost 31 | 32 | The [boost](https://boost.org) library offers various algorithms that are already implemented and you only need to pick and combine. It is very useful to be aware that there is a [BGL table of contents](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/table_of_contents.html) which is available during the exam. 33 | 34 | It's important to know what they already have, so you don't end up implementing dijstra's shortest path algorithm yourself... again. 35 | 36 | If you like videos, there's a [good one here](https://www.youtube.com/watch?v=GSp2531Wti4) that covers the basics. 37 | 38 | ## Algorithms 39 | 40 | Some algorithms you should know exist follow. I'll get to how to use them later. 41 | 42 | * Maximum Flow and Matching Algorithms 43 | 1. [`edmonds_karp_max_flow`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/edmonds_karp_max_flow.html) 44 | 2. [`push_relabel_max_flow`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/push_relabel_max_flow.html) 45 | 3. [`edmonds_maximum_cardinality_matching`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/maximum_matching.html) 46 | 4. [`maximum_weighted_matching`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/maximum_weighted_matching.html) 47 | * Minimum Spanning Tree Algorithms 48 | 1. [`kruskal_minimum_spanning_tree`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/kruskal_min_spanning_tree.html) 49 | 2. [`prim_minimum_spanning_tree`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/prim_minimum_spanning_tree.html) 50 | * Minimum Cost Maximum Flow Algorithms 51 | 1. [`cycle_canceling`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/cycle_canceling.html) 52 | 2. [`successive_shortest_path_nonnegative_weights`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/successive_shortest_path_nonnegative_weights.html) 53 | * Connected Components Algorithms 54 | 1. [`connected_components`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/connected_components.html) 55 | 2. [`strong_components`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/strong_components.html) 56 | 3. [`biconnected_components`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/biconnected_components.html) 57 | 4. [`articulation_points`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/biconnected_components.html#sec:articulation_points) 58 | * [`dijkstra_shortest_paths`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/dijkstra_shortest_paths.html) 59 | 60 | ## Building A Graph 61 | 62 | There are different graph types, but for Algolab you will use the same one almost always: [adjacency_list](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/using_adjacency_list.html) with underlying storage `boost::vecS` for the Vertex List and Edge List. 63 | 64 | There is also the option of using an `adjacency_matrix` instead of an `adjacency_list` but I have actually never done that during the whole course. 65 | 66 | The type of the Graph looks somewhat scary when you're not used to it: 67 | 68 | ```c++ 69 | typedef boost::adjacency_list< 70 | boost::vecS, 71 | boost::vecS, 72 | boost::undirectedS> graph_t; 73 | ``` 74 | 75 | The options for the underlying types of the Edge List and Vertex List are the first two template arguments. They have an effect on speed, memory consumption, and on which operations break the iterators over vertices or edges. On [this page](https://www.boost.org/doc/libs/1_58_0/libs/graph/doc/adjacency_list.html) you can find a table that tells you more on that. Some facts that I found somewhere: 76 | 77 | * If you don't want parallel edges (two edges with the same source and target) you could use a `setS` or `hash_setS` type for the Edge List. If you don't care, use a sequence based type like `listS`, `vecS`, or `slistS` because `add_edge()` is faster there. 78 | However, finding an edge is faster in AssociativeContainers and hence clearing a vertex also is. 79 | 80 | * The iterator over edges is fastest with `vecS` and slowest with `hash_setS`. 81 | 82 | * My notes say 83 | 84 | > Use `listS`for VertexList (the second type argument). It uses more space than `vecS` but is constant time instead of `O(V+E)` for `remove_vertex()` and it does not cause invalidation of vertex descriptors on deletion. 85 | 86 | although looking back, I think I never heeded my own advice and just used `vecS`for everything. 87 | 88 | ### Directedness 89 | 90 | A graph is either `directedS`, `undirectedS`, or `bidirectionalS`. The last one stores all the information twice. Only in that last case can you get the *incoming* edges of a vertex. But you don't need that often. 91 | 92 | ### Weights / Internal Properties 93 | 94 | Some algorithms work with special properties like "Vertex Color" or "Edge Weight" etc. The simplest way to make them work is by using a predefined type in the graph typedef to tell boost that we need a graph with that property. For example, let's try run [kruskal's algorithm](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/kruskal_min_spanning_tree.html) for building a minimum spanning tree. 95 | 96 | For this we make use of some (optional) template arguments that come after the ones I've already mentioned. 97 | 98 | ```c++ 99 | typedef boost::adjacency_list< 100 | boost::vecS, 101 | boost::vecS, boost::undirectedS, 102 | boost::no_property, 103 | boost::property 104 | > weighted_graph_t; 105 | ``` 106 | 107 | We define the `Vertex Property` to be unset by specifying `boost::no_property` because we don't care about that. And for the edges, we want a weight. 108 | 109 | Note that this snippet uses `int` as a type for the edge weight. But sometimes we need a larger type. There's no problem in changing this to a `long`! 110 | 111 | The `boost::property` syntax here seems unneccessary bloat at first, but once we would like to have more than just one property associated with the Edge, it makes sense: The `boost::property` actually takes three template arguments if you give it three. The first is the kind of property (`edge_weight_t` here), the second is the type of that property's value (`int` here) and the third, which I have not yet shown, is... *another property*. That's right, template recursion! 112 | 113 | We could then start nesting them like this: 114 | 115 | ```c++ 116 | typedef boost::adjacency_list>>> graph_t; 121 | ``` 122 | 123 | But you shouldn't have to worry too much about writing these yourself. The spirit of Algolab is to copy-paste such examples from the slides or the code examples. 124 | 125 | Once you have built a graph with such internal properties as required by the function you want to call, you can call it quite simply and it will automatically use these properties. For example after a quick look at the [kruskal](https://stackoverflow.com/a/56124666/2550406) docs, I add the include from "Where Defined" and call it like this: 126 | 127 | ```c++ 128 | #include 129 | // [Graph Type Definition Here] 130 | 131 | // now inside some function: 132 | boost::kruskal_minimum_spanning_tree ( 133 | G /* our graph */, 134 | out_it /* where it should write the output to */ ) 135 | ``` 136 | 137 | But we don't know yet what that `out_it` is supposed to be. More on that in the section on [calling kruskal](#calling-kruskal) below. 138 | 139 | ### Inserting Vertices 140 | 141 | When you have your typedef the way you want it, it's quite easy to instantiate a graph variable of that type with a given number of vertices. 142 | 143 | ```c++ 144 | int main(){ 145 | graph_t G(100); 146 | return 0; 147 | } 148 | ``` 149 | 150 | Every vertex is identified by some vertex handle. The type for that can be typedef'd e.g. with 151 | 152 | ```c++ 153 | typedef boost::graph_traits::vertex_descriptor vertex_desc; 154 | ``` 155 | 156 | but in practise as long as your underlying types are always `vecS`, these are actually just integer values. Makes sense, because the vertices are stored in a vector, right? 157 | 158 | When you delete a vertex from the graph, all the vertices after that one are shifted around, so deleting a vertex can invalidate your vertex descriptor. The solution for algolab is to just not do that. 159 | 160 | ### Inserting Edges 161 | 162 | ```c++ 163 | int n = 100; 164 | graph_t G(n); 165 | // add an edge from vertex 0 to vertex 4 166 | boost::add_edge(0, 4, G); 167 | // adding an edge automatically adds the vertex if necessary. 168 | // But why would you not set the correct graph size from the start? 169 | boost::add_edge(0, 150, G); 170 | ``` 171 | 172 | > ``` 173 | > std::pair 174 | > add_edge(vertex_descriptor u, vertex_descriptor v, 175 | > adjacency_list& g) 176 | > ``` 177 | > 178 | > Adds edge *(u,v)* to the graph and returns the edge descriptor for the new edge. For graphs that do not allow parallel edges, if the edge is already in the graph then a duplicate will not be added and the `bool` flag will be `false`. When the flag is `false`, the returned edge descriptor points to the already existing edge. 179 | 180 | The `add_edge` function returns an edge descriptor. That allows us to later access that edge. For example if we want to modify its weight. 181 | 182 | If we have our weighted graph again, with only one edge property, then we can set that property right when adding the edge. 183 | 184 | ```c++ 185 | boost::add_edge(u, v, weight, G); 186 | ``` 187 | 188 | Other times, when we have many properties per edge, I find it easier to do it in a different way. We can kindly ask boost to give us a "property map" which we can then use to look up a specific property for an edge descriptor. 189 | 190 | ```c++ 191 | // type of edge descriptor 192 | typedef boost::graph_traits::edge_descriptor edge_desc; 193 | // ... 194 | auto weight_map = boost::get(boost::edge_weight, G); 195 | // Note how we get the property map for edge_weight, 196 | // not for edge_weight_t. Because the _t was only in the typedefs 197 | 198 | auto edge_one_pair = boost::add_edge(0, 1, G); 199 | auto edge_two_pair = boost::add_edge(2, 2, G); 200 | // these are pairs of an edge descriptor and a boolean 201 | // but "auto" can save you a lot of typing and confusion 202 | 203 | // set weight to 12 for the first edge 204 | edge_desc e1 = edge_one_pair.first; 205 | weight_map[e1] = 12; 206 | ``` 207 | 208 | ### Custom (Bundled) Properties 209 | 210 | Sometimes we want to store additional information with an edge or a vertex. For example some `id` or whether we have already visited that vertex/edge. 211 | 212 | The simplest way to do that is to specify a struct or class as the last of the recursive properties: 213 | 214 | ```c++ 215 | typedef struct { 216 | uint id; 217 | } EdgeData; 218 | 219 | class VertexData { 220 | public: 221 | bool visited; 222 | }; 223 | 224 | typedef boost::adjacency_list 229 | > 230 | weighted_graph; 231 | ``` 232 | 233 | Then, we can access these values using the very simple syntax 234 | 235 | ```c++ 236 | G[my_vertex_descriptor].visited = true; 237 | uint value = G[my_edge_descriptor].id; 238 | ``` 239 | 240 | See also [here](https://stackoverflow.com/questions/31540115/boost-graph-getting-adjacent-vertices) and [here](https://stackoverflow.com/a/47573336/2550406) on stackoverflow. 241 | 242 | ### Iterating 243 | 244 | There are functions to get all incoming edges, all outgoing edges, all neighbouring vertices, all vertices, and so on. All of these give you the descriptors you need for accessing such things. 245 | 246 | When you have an edge, you can get the vertices it connects with `boost::source(edge, G)` and `boost::target(edge, G)`. 247 | 248 | One way to iterate over all outgoing edges from a vertex (you can also use this on an undirected graph): 249 | 250 | ```c++ 251 | auto it_pair = boost::out_edges(my_vertex_desc, G); 252 | for ( auto it = it_pair.first; it != it_pair.second; it++){ 253 | // do stuff until it_pair.first has reached the 254 | // end which is stored in it_pair.second 255 | } 256 | ``` 257 | 258 | I've come to prefer this alternative way: 259 | 260 | ```c++ 261 | #include 262 | for ( auto it : boost::make_iterator_range(boost::out_edges(my_vertex_desc, G))){ 263 | // do stuff 264 | } 265 | ``` 266 | 267 | I can't tell you why I prefer that, though. Probably because there are fewer ways to mess this one up. 268 | 269 | ### Graph `add_edge` with properties 270 | 271 | `boost::add_edge(from, to, propertyvalue, graph)` where `propertyvalue` is [optional](https://clickhouse.tech/codebrowser/html_report/ClickHouse/contrib/boost/boost/graph/detail/adjacency_list.hpp.html#_ZN5boost8add_edgeENT_17vertex_descriptorES1_RKNS0_18edge_property_typeERNS_21directed_graph_helperIS0_EE). So if the EdgeProperty in the graph typedef is a struct, it can be created and passed there. If it is an integer like `boost::edge_weight_t` then it can be directly passed. For composite properties it must be composite as well. 272 | 273 | Alternatively, simply first add the edge and then set the properties by using the various property maps and the edge descriptor. You get the edge descriptor from the pair that is returned by `add_edge`: 274 | 275 | > ``` 276 | > std::pair 277 | > add_edge(vertex_descriptor u, vertex_descriptor v, 278 | > adjacency_list& g) 279 | > ``` 280 | > 281 | > Adds edge *(u,v)* to the graph and returns the edge descriptor for the new edge. For graphs that do not allow parallel edges, if the edge is already in the graph then a duplicate will not be added and the `bool` flag will be `false`. When the flag is `false`, the returned edge descriptor points to the already existing edge. 282 | > 283 | > The placement of the new edge in the out-edge list is in general unspecified, though ordering of the out-edge list can be accomplished through the choice of `OutEdgeList`. If the `VertexList` selector is `vecS`, and if either vertex descriptor `u` or `v` (which are integers) has a value greater than the current number of vertices in the graph, the graph is enlarged so that the number of vertices is `std::max(u,v) + 1`. 284 | > 285 | > If the `OutEdgeList` selector is `vecS` then this operation will invalidate any `out_edge_iterator` for vertex *u*. This also applies if the `OutEdgeList` is a user-defined container that invalidates its iterators when `push(container, x)` is invoked (see Section [Customizing the Adjacency List Storage](https://www.boost.org/doc/libs/1_37_0/libs/graph/doc/using_adjacency_list.html#sec:custom-storage)). If the graph is also bidirectional then any `in_edge_iterator` for *v* is also invalidated. If instead the graph is undirected then any `out_edge_iterator` for *v* is also invalidated. If instead the graph is directed, then `add_edge()` also invalidates any `edge_iterator`. 286 | > 287 | > ------ 288 | > 289 | > ``` 290 | > std::pair 291 | > add_edge(vertex_descriptor u, vertex_descriptor v, 292 | > const EdgeProperties& p, 293 | > adjacency_list& g) 294 | > ``` 295 | > 296 | > Adds edge *(u,v)* to the graph and attaches `p` as the value of the edge's internal property storage. Also see the previous `add_edge()` member function for more details. 297 | 298 | 299 | 300 | I think the [property constructor](https://www.boost.org/doc/libs/1_60_0/libs/graph/doc/property.html)`property(const T& v, const NextProperty& b);` accepts nested properties similarly to the typedef. 301 | 302 | On that note, for the graph EdgeProperty typedefs, the predefined tags are, also from that link: 303 | 304 | ```c++ 305 | The following property tags are defined in boost/graph/properties.hpp. 306 | namespace boost { 307 | enum edge_name_t { edge_name }; 308 | enum edge_weight_t { edge_weight }; 309 | enum edge_index_t { edge_index }; 310 | enum edge_capacity_t { edge_capacity }; 311 | enum edge_residual_capacity_t { edge_residual_capacity }; 312 | enum edge_reverse_t { edge_reverse }; 313 | enum vertex_name_t { vertex_name }; 314 | enum vertex_distance_t { vertex_distance }; 315 | enum vertex_index_t { vertex_index }; 316 | enum vertex_color_t { vertex_color }; 317 | enum vertex_degree_t { vertex_degree }; 318 | enum vertex_out_degree_t { vertex_out_degree }; 319 | enum vertex_in_degree_t { vertex_in_degree }; 320 | enum vertex_discover_time_t { vertex_discover_time }; 321 | enum vertex_finish_time_t { vertex_finish_time }; 322 | enum graph_name_t { graph_name }; 323 | 324 | BOOST_INSTALL_PROPERTY(vertex, index); 325 | BOOST_INSTALL_PROPERTY(edge, index); 326 | // ... 327 | } 328 | ``` 329 | 330 | 331 | 332 | ## Calling 333 | 334 | You know now how to build a graph using internal properties. They will automatically be used when you call a BGL function like [`kruskal_minimum_spanning_tree`](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/kruskal_min_spanning_tree.html). But what about the other arguments? 335 | 336 | The first argument is the graph, the second is an Output Iterator according to the docs, so ignoring all the optional parameters we could call it like this: 337 | 338 | ```c++ 339 | std::vector mst; 340 | boost::kruskal_minimum_spanning_tree(G, std::back_inserter(mst)); 341 | ``` 342 | 343 | ### Named Parameters 344 | 345 | Many boost functions have in their declaration an optional parameter of type`bgl_named_params`: 346 | 347 | ```c++ 348 | template < class Graph, class OutputIterator, 349 | class P, class T, class R > OutputIterator 350 | 351 | kruskal_minimum_spanning_tree( 352 | Graph& g, OutputIterator tree_edges, 353 | const bgl_named_params& params = all defaults); 354 | ``` 355 | 356 | You can ignore that option if you want to. 357 | 358 | What they do is outlined further down in the docs and they either specify `IN` (allow you to give additional inputs) or `OUT` (allow you to read additional outputs) or `UTIL`(used internally). 359 | 360 | For example you might want to get a distance map from [Prim's algorithm](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/prim_minimum_spanning_tree.html) so that you know the weight of the edges in the MST. That is supposed to be a Property Map that hands you the distance from a given vertex to its direct parent in the MST. We can create such a map in multiple ways - see below. Or perhaps you want to tell Prim's algorithm where to start at, using the named parameter `root_vertex` that is also documented on that page. 361 | 362 | You can do that like this: 363 | 364 | ```c++ 365 | std::vector pred_map(n); 366 | auto p = boost::make_iterator_property_map(pred_map.begin(), 367 | boost::get(boost::vertex_index, G)); 368 | /*std::vector distmap(n); 369 | auto d = boost::make_iterator_property_map(distmap.begin(), 370 | boost::get(boost::vertex_index, G));*/ 371 | boost::prim_minimum_spanning_tree(G, p, boost::root_vertex(tat)/*.distance_map(d)*/); 372 | ``` 373 | 374 | The commented-out part shows you how to use more than one named parameter. 375 | 376 | ### Custom Property Map (Vector) 377 | 378 | ```c++ 379 | #include 380 | auto propmap = boost::make_iterator_property_map(myvec.begin(), boost::get(boost::vertex_index, G)); 381 | ``` 382 | 383 | Writing to the newly created property map with the vertex as index will first look up the vertex in the second argument and because we specified a property map there that retrieves the `vertex_index`, it looks up the `vertex_index`. With `vecS` as vertex list in the graph, the `vertex_index` of a graph is often simply the `vertex_descriptor` but this here is how it's done correctly. 384 | 385 | Then that index is used to perform an access to the vector `myvec` at that index. So modifying `propmap` will modify the vector. 386 | 387 | Note: This would not work with edges that easily. Because edges don't have an `edge_index` by default. You would have to add that `boost::edge_index_t` property in the graph typedef and also set them yourself for each edge you add. Then you could use the same concept. 388 | 389 | ### Custom Property Map (Lambda) 390 | 391 | ```c++ 392 | // https://stackoverflow.com/questions/26979585/is-it-possible-to-have-several-edge-weight-property-maps-for-one-graph 393 | // You can compose a property map in various ways. The simplest approach would seem something like: 394 | 395 | #include 396 | auto wmap1 = make_function_property_map([&weight_data](unsigned vertex_id) { return weight_data.at(vertex_id).weight1; }); 397 | ``` 398 | 399 | We can even use it for writing to! E.g. with a vector backing it as storage: 400 | 401 | ```c++ 402 | std::vector compmap(mbridges); // storage 403 | 404 | // iterator property map (alternative) 405 | auto propmap = boost::make_iterator_property_map( 406 | compmap.begin(), boost::get(boost::edge_index, OG)); 407 | 408 | // function property map 409 | auto funmap = boost::make_function_property_map( 410 | [&OG, &compmap](edge_desc edge)->unsigned long&{ 411 | // use edge_id as index in vector and return writeable reference 412 | size_t index = boost::get(boost::edge_index, OG)[edge]; 413 | return compmap[index]; 414 | }); 415 | // use the map 416 | unsigned long nbc = boost::biconnected_components(OG, funmap /* could also use propmap here */); 417 | 418 | ``` 419 | 420 | The second template argument is not necessary, because it can be inferred from the return type of the lambda. The lambda must have a return type that is a non-const reference, otherwise it cannot be written to - only read - and hence is not an lvalue. Final note: the lambda does not use an `&` in the return statement. 421 | 422 | ### Many Weight Maps for same Graph 423 | 424 | use an edge property struct that defines some `id` for edges so you can get a hashable key from a given edge descriptor. Then store your things in an ordinary `unordered_map` and when an algorithm wants a weight map, provide it as 425 | 426 | ```c++ 427 | auto wmap = boost::make_function_property_map ( 428 | [&weight_storage, i, &G](edge_desc e){ 429 | uint edge_id = G[e].id; 430 | return weight_storage[i][edge_id]; 431 | }); 432 | 433 | ``` 434 | 435 | where `weight_storage[i]` is your map. 436 | 437 | Reminder: specify the edge properties as third template argument of `boost::property` if you are using builtin properties as well: 438 | 439 | ```c++ 440 | typedef struct { 441 | uint id; 442 | } EdgeData; 443 | 444 | typedef boost::adjacency_list > weighted_graph; 446 | ``` 447 | 448 | ### Filtered Graph 449 | 450 | Allows you to filter the graph without copying it. Except that this is usually slow, because the predicates will be potentially executed every time an edge is accessed. So in the end I just used this for simpler graph copying. 451 | 452 | This can also be useful when giving a graph to a visitor to operate on, but that is more complexity than I hope you will need during the exam. 453 | 454 | [Example of DFS on MST](https://stackoverflow.com/a/49429372/2550406) uses `boost::keep_all` to keep all vertices in the filtered graph but only some edges. 455 | 456 | > I just want to comment on one API "limitation" of filtered_graphs. `num_vertices(filtered_graph)` and `num_edges(filtered_graph)` will return the num of nodes and edges of the original graph. You would need to use `boost::vertices(filtered_graph)` and `boost::nodes(filtered_graph)` and count them to get the right value 457 | 458 | If you're using a filtered graph in another algo, it might be [faster](https://stackoverflow.com/a/35570685/2550406) to just copy it. 459 | The boost docs on [filtered_graph](https://www.boost.org/doc/libs/1_75_0/libs/graph/doc/filtered_graph.html) contain an example, so filtering graphs is actually possible to do during the exam with relative ease. Do note that you need their typedefs and prepend a few `boost::` at times. 460 | 461 | #### Example by Me 462 | 463 | ```c++ 464 | typedef boost::adjacency_list > bip_graph; 466 | typedef boost::property_map::type EdgeWeightMap; 467 | 468 | template 469 | struct positive_edge_weight { // misleading naming because why not 470 | positive_edge_weight() { } 471 | positive_edge_weight(EdgeWeightMap weight) : m_weight(weight) { } 472 | long dist = 0; 473 | template 474 | bool operator()(const Edge& e) const { 475 | return get(m_weight, e) <= dist; 476 | } 477 | EdgeWeightMap m_weight; 478 | }; 479 | 480 | // later: 481 | positive_edge_weight filter(boost::get(boost::edge_weight, F)); 482 | filter.dist = time_allowed; 483 | boost::filtered_graph > 484 | fg(F, filter); 485 | bip_graph H; boost::copy_graph(fg, H); 486 | ``` 487 | 488 | #### Example with Struct (Vertices and Edges) 489 | 490 | Code by [sehe](https://stackoverflow.com/a/45850742/2550406), CC-BY-SA 491 | 492 | ```c++ 493 | struct Predicate { // both edge and vertex 494 | bool operator()(Graph::edge_descriptor) const { return true; } // all 495 | bool operator()(Graph::vertex_descriptor vd) const { return suppressed_->count((*g)[vd].label) == 0; } 496 | 497 | Graph* g; 498 | labels* suppressed_; 499 | } predicate {&g, &suppressed}; 500 | 501 | using Filtered = boost::filtered_graph; 502 | Filtered fg(g, predicate, predicate); 503 | ``` 504 | 505 | #### Example with Lambda 506 | 507 | ```c++ 508 | #include 509 | auto myfilter = [&F, time_allowed](bip_edge_desc e){ 510 | return boost::get(boost::edge_weight, F)[e] <= time_allowed; }; 511 | boost::filtered_graph> fg(F, myfilter); 512 | bip_graph H; boost::copy_graph(fg, H); 513 | ``` 514 | 515 | Note that the explicit type of `std::function` as opposed to just using `decltype(myfilter)` is required because the latter is not default-constructible. 516 | 517 | ## Draw Graph For Debugging (Graphviz) 518 | 519 | This allows you to generate output in `dot`. Which you can then render to an svg and view it in firefox (for example) like this: 520 | 521 | ```bash 522 | dot -Tsvg -omygraph.svg mygraph.dot && firefox mygraph.svg 523 | ``` 524 | 525 | This works also in the exam environment. But I hope you won't need it. 526 | 527 | [SO:](https://stackoverflow.com/questions/9181183/how-to-print-a-boost-graph-in-graphviz-with-one-of-the-properties-displayed)The correct way to get property map from your bundled properties is to use `boost::get`. So you can do something like: 528 | 529 | ```c++ 530 | #include 531 | boost::write_graphviz(std::cout, your_graph, 532 | boost::make_label_writer(boost::get(&Vertex::name, your_graph)), 533 | boost::make_label_writer(boost::get(&Edge::weight, your_graph)) 534 | ); 535 | ``` 536 | 537 | Where `your_graph` is the graph object you have created. 538 | 539 | Or to a file: 540 | 541 | ```c++ 542 | std::vector NameVec; // vector of names 543 | // write the dot file 544 | std::ofstream dotfile (strDotFile.c_str ()); 545 | write_graphviz (dotfile, g, make_label_writer(&NameVec[0])); 546 | ``` 547 | 548 | [Or using dynamic properties](https://stackoverflow.com/a/60114442/2550406) for dot: 549 | 550 | ```c++ 551 | #include 552 | boost::dynamic_properties dp; 553 | dp.property("node_id", get(boost::vertex_index, g)); 554 | dp.property("weight", get(boost::edge_weight, g)); 555 | boost::write_graphviz_dp(std::cout, g, dp); 556 | ``` 557 | 558 | Note that there is no vertex_index if you're using `listS`. See further below under Combining A Bundle for the alternative way. 559 | 560 | #### Combining a Bundle 561 | 562 | ```c++ 563 | dp.property("node_id", get(boost::vertex_index, g)); 564 | auto vbundle = get(boost::vertex_bundle, g); 565 | dp.property("label", 566 | boost::make_transform_value_property_map([](VertexClass const& vd) { 567 | return vd.get_id(); 568 | }, vbundle)); 569 | ``` 570 | 571 | #### or **simpler**: 572 | 573 | ```c++ 574 | #include 575 | boost::dynamic_properties dp; 576 | dp.property("node_id", get(boost::vertex_index, G)); 577 | dp.property("label", get(boost::edge_capacity, G)); 578 | write_graphviz_dp(std::cout, G, dp); 579 | ``` 580 | 581 | #### or with lambdas 582 | 583 | ```c++ 584 | #include 585 | #include 586 | // visualize graph 587 | boost::dynamic_properties dp; 588 | dp.property("node_id", boost::make_function_property_map( 589 | [nts, nms](vertex_desc vid) { 590 | if (vid == 0) { return std::string("source"); } 591 | if (vid == 1) { return std::string("sink"); } 592 | if (vid > 1 && vid < nts+2) { return "Station " + std::to_string(((int)vid)-2); } 593 | return "Agent " + std::to_string(((int)vid) - nts - 2); 594 | })); 595 | dp.property("label", boost::make_function_property_map( 596 | [MAX, &G](edge_desc eid) { 597 | unsigned long prio = MAX - boost::get(boost::edge_weight, G)[eid]; 598 | unsigned long capa = boost::get(boost::edge_capacity, G)[eid]; 599 | return "( c=" + std::to_string(capa) + ", p=" + std::to_string(prio) + ")"; 600 | })); 601 | std::ofstream fil("graph"+std::to_string(curr_test)+".dot"); 602 | boost::write_graphviz_dp(fil, G, dp); 603 | 604 | ``` 605 | 606 | #### Coloring 607 | 608 | https://stackoverflow.com/a/48371770/2550406 609 | 610 | ## Boost Variant and Boost Any 611 | 612 | [Docs](https://www.boost.org/doc/libs/1_75_0/doc/html/variant/misc.html#variant.versus-any) say that `boost::variant` avoids dynamic allocation overhead and only allows specified types whereas `boost::any` allows any type and has not much template magic - avoiding compile-time processor and memory demands. 613 | 614 | ```c++ 615 | #include 616 | #include 617 | ``` 618 | 619 | ## Disjoint Sets / Cluster Merging 620 | 621 | [incremental components](https://www.boost.org/doc/libs/1_31_0/libs/graph/doc/incremental_components.html) and [disjoint sets](https://algolab.inf.ethz.ch/doc/boost/doc/libs/1_74_0/libs/graph/doc/disjoint_sets.html) (for doing cluster merges myself). Also see [disjoint sets with storage](https://github.com/chaehni/AlgoLab/blob/master/2019/week13/potw%20-%20revenge%20of%20the%20sith/src/algorithm.cpp#L38) which looks easier. 622 | using it with just `int` as ids seems easiest, but we can also provide boost property maps for mapping to indexes and back. When used with ints, no mapping is required. 623 | But need to track sizes of sets myself. And make sure to use new find_set after creating a merged set. 624 | 625 | ```c++ 626 | #include 627 | boost::disjoint_sets_with_storage<> uf(n); // initialized with n sets 628 | uf.union_set(a, b); // union directly, using elements 629 | uf.link(rep1, rep2); // union using representatives 630 | int rep1 = uf.find_set(12); // getting representatives 631 | ``` 632 | 633 | ### 634 | 635 | ## Serializing a Graph 636 | 637 | https://stackoverflow.com/a/28974945/2550406 638 | 639 | ## How To CGAL 640 | 641 | There are mostly three things we do with that library: 642 | 643 | * Delaunay Triangulations 644 | * Linear Programs 645 | * Something with geometry and exact number types. 646 | 647 | ### Delaunay 648 | 649 | #### Store Info 650 | 651 | Copy paste the code for the typedefs from the last slide of the Delaunay slides or from the emst example. Then adapt the includes and typedefs so you can store a struct with each Vertex and / or each Face: 652 | 653 | ```c++ 654 | #include 655 | #include 656 | #include 657 | #include 658 | #include 659 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 660 | class VertexInfo{ 661 | public: 662 | uint id; 663 | }; 664 | class FaceInfo{}; 665 | // we want to store an index with each vertex 666 | typedef CGAL::Triangulation_vertex_base_with_info_2 Vb; 667 | typedef CGAL::Triangulation_face_base_with_info_2 Fb; 668 | typedef CGAL::Triangulation_data_structure_2 Tds; 669 | typedef CGAL::Delaunay_triangulation_2 Triangulation; 670 | typedef Triangulation::Face_handle Fh; 671 | typedef Triangulation::Vertex_handle Vh; 672 | typedef K::Point_2 P; 673 | ``` 674 | 675 | #### Read Points 676 | 677 | ```c++ 678 | K::Point_2 p; 679 | std::cin >> p; 680 | ``` 681 | 682 | This works. No need to first store both coordinates in ints. 683 | 684 | #### Insert Points 685 | 686 | There's a big speed difference between the different possibilities. 687 | 688 | Ideally, you fill a vector `v` with all the points and let CGAL handle the ordering. (Instead of inserting one after the other. In that case you should maybe shuffle them to avoid adversarial inputs that decrease the runtime enormously!). 689 | 690 | You [can](https://doc.cgal.org/latest/Triangulation_2/classCGAL_1_1Triangulation__2.html#ac5e9bc8adef80dc01a0b31c2d0234545) store the `VertexInfo` defined above with each Point in a Pair in the vector if you want to: 691 | 692 | ```c++ 693 | Triangulation tri; 694 | std::vector > vec; 695 | // [fill vec] 696 | tri.insert(vec.begin(), vec.end()); 697 | ``` 698 | 699 | #### Documentation 700 | 701 | Relevant are 702 | 703 | * [Delaunay_triangulation_2](https://doc.cgal.org/latest/Triangulation_2/classCGAL_1_1Delaunay__triangulation__2.html) 704 | * In the sidebar a bit below it [Triangulation_2](https://doc.cgal.org/latest/Triangulation_2/classCGAL_1_1Triangulation__2.html). The Delaunay Triangulation has all those functions as well. 705 | * [Triangulation_data_structure_2](https://doc.cgal.org/latest/TDS_2/classTriangulationDataStructure__2.html) with some weird concepts. 706 | 707 | #### Faces 708 | 709 | if you have a `Triangulation::Face_handle`, you can use the `->` operator to get the [neighbor](https://doc.cgal.org/latest/TDS_2/classTriangulationDataStructure__2_1_1Face.html#a096a50bd226daf09826eeacf54727f0e) face at an index between 0 and 2 (including). 710 | 711 | That neighbour face at index $i$ is the one that is adjacent to edge $i$, which is opposite to the vertex $i$. You can get that [vertex](https://doc.cgal.org/latest/TDS_2/classTriangulationDataStructure__2_1_1Face.html#ac4d03671704cd164b279706a098fdf5e) handle with the index as well. `fh->vertex(1)`. Have a look at the `fh->cw()` and `fh->ccw()` method for juggling with those indices. 712 | 713 | [circumcenter](https://doc.cgal.org/latest/Triangulation_2/classCGAL_1_1Triangulation__2.html#a4185c75ba2c5ec34181fdef8fa57401c) can be useful. 714 | 715 | #### Locating 716 | 717 | You should know the methods `locate()` and `nearest_vertex()`! 718 | 719 | #### Edges 720 | 721 | Edges are just a pair of a Face handle and an index. You can use `.first` and `.second` to get the face and the edge index $i$. 722 | 723 | The triangulation has the method [mirror_edge](https://doc.cgal.org/latest/Triangulation_2/classCGAL_1_1Triangulation__2.html#ab97ce60b20674d0a7a4455e88c2eadb1) which gives you the same edge again, but from perspective of the neighbouring face. 724 | 725 | tri.[segment](https://doc.cgal.org/latest/Triangulation_2/classCGAL_1_1Triangulation__2.html#a06f3967c92db0fe28368c31ff671c658) can be useful to compute the length if you don't want to use `CGAL::squared_distance` on the two endpoints for some reason. 726 | 727 | #### Circulators 728 | 729 | They are like iterators, but scary. E.g. `incident_faces` and `incident_vertices` are returning circulators. 730 | 731 | * They can be null if you only have one Point in the triangulation 732 | * They make it easy to do something wrong in the loop condition. Remember to increment them, always. 733 | * They can give you something infinite. 734 | * Use a `do...while` loop. *if* it is not null. 735 | * Stop the loop once the circulator that you incremented is equal to the original circulator. 736 | 737 | #### Slides 738 | 739 | Some of the slides are very good for this topic! Definitely check them out, since they might have been updated for the latest version of CGAL. 740 | 741 | ### 742 | 743 | ![image-20210108111915037](README.assets/image-20210108111915037.png) 744 | 745 | and query in `log (n)` where `n` are the already-stored points. 746 | 747 | Note: 748 | 749 | * "each point has an edge to all closest other points". 750 | 751 | ![image-20201117155357857](README.assets/image-20201117155357857.png) 752 | 753 | ![image-20210108113111997](README.assets/image-20210108113111997.png) 754 | 755 | ![image-20210108113252520](README.assets/image-20210108113252520.png) 756 | 757 | ![image-20210121094034950](README.assets/image-20210121094034950.png) 758 | 759 | #### Build Graph from Delaunay 760 | 761 | ```c++ 762 | // construct triangulation 763 | Triangulation t; 764 | t.insert(points.begin(), points.end()); 765 | 766 | // construct graph from the triangulation using only edges with distance <= r 767 | graph g(n); 768 | for (Triangulation::Finite_edges_iterator e = t.finite_edges_begin(); e != t.finite_edges_end(); ++e) { 769 | K::Segment_2 seg = t.segment(*e); 770 | K::Point_2 s = seg.source(), t = seg.target(); 771 | if (CGAL::squared_distance(s, t) > r*r) continue; 772 | 773 | boost::add_edge(indices.at(s), indices.at(t), g); 774 | } 775 | ``` 776 | 777 | Do note however that sometimes you don't even need to build a new graph. You can simply traverse the delaunay graph directly. 778 | 779 | #### Convex Hull 780 | 781 | You can get the convex hull by looking at the neighbours of the infinite vertex. 782 | 783 | > ![image-20201207183451370](README.assets/image-20201207183451370.png) 784 | 785 | ### 786 | 787 | ### Linear Programs 788 | 789 | I think the slides suffice on this. I will have some common pitfalls regarding this though. 790 | 791 | Here are some good quotes from the slides: 792 | 793 | ![image-20201029143254630](README.assets/image-20201029143254630.png) 794 | 795 | ![image-20201029143621938](README.assets/image-20201029143621938.png) 796 | 797 | ##### Pivot Rule 798 | 799 | Use the default pivot rule unless it fails. If it fails, it cycles forever. 800 | 801 | ```c++ 802 | // slower but certain to avoid cycling: 803 | CGAL::Quadratic_program_options options; options.set_pricing_strategy(CGAL::QP_BLAND); Solution s = CGAL::SOLVER(program, ET(), options); 804 | ``` 805 | 806 | ##### Code 807 | 808 | ```c++ 809 | #include 810 | #include 811 | #include 812 | // choose input type (input coefficients must fit) 813 | typedef int IT; 814 | // choose exact type for solver (CGAL::Gmpz or CGAL::Gmpq) 815 | typedef CGAL::Gmpz ET; 816 | 817 | // program and solution types 818 | typedef CGAL::Quadratic_program Program; 819 | typedef CGAL::Quadratic_program_solution Solution; 820 | ``` 821 | 822 | ![image-20201029144113354](README.assets/image-20201029144113354.png) 823 | 824 | ![image-20201029144338585](README.assets/image-20201029144338585.png) 825 | 826 | ![image-20201029151434018](README.assets/image-20201029151434018.png) 827 | 828 | There is also a nonnegative linear solver `CGAL::solve_nonnegative_linear_program(lp, ET());` but it ignores all other lower and upper bounds set with `set_l` and `set_u`. 829 | 830 | ![image-20201029153914441](README.assets/image-20201029153914441.png) 831 | 832 | > Do not copy or assign objects of type CGAL::Quadra.c_program_solution 833 | > (source is new slides. but CGAL [docs](https://doc.cgal.org/latest/QP_solver/classCGAL_1_1Quadratic__program__solution.html) say that works) 834 | 835 | ##### Too Slow? 836 | 837 | * Use `QP_Bland` as per pivot rule to avoid cycles, although its generally slower. 838 | * If that doesn't help, maybe switch up your input types. `double` is faster than `int`, and `int` is faster than `K::FT`. 839 | 840 | > We use `int` as the input type, and `MP_Float` or `Gmpz` (which is faster and preferable if `GMP` is installed) as the exact type for the internal computations. In larger examples, it pays off to use `double` as input type in order to profit from the automatic floating-point filtering that takes place then. 841 | > [Source]( 842 | 843 | ### Geometry 844 | 845 | * Geometry is slow. So it can pay off to avoid constructions. 846 | * Use the worst possible kernel, it's the fastest as well. 847 | * Lower numbers are generally faster than higher numbers. 848 | * To avoid using square root, just work with the squares. 849 | * Don't construct intersections directly - first check if they actually intersect. That's faster. 850 | * [Some helpful functions](https://doc.cgal.org/latest/Kernel_23/group__kernel__global__function.html) 851 | 852 | ### Varia 853 | 854 | I don't think you really need that. But here it is. 855 | 856 | #### Faces Iterator Range 857 | 858 | CGAL Version 5 seems to have `for (Face_handle fh : triangulation.all_face_handles())`. The Version 4 I have in my docker locally does not support that though, so there I must do 859 | 860 | ```c++ 861 | for ( auto fit = tri.all_faces_begin(); fit != tri.all_faces_end(); fit++ ){ 862 | Fh fh = fit; 863 | ``` 864 | 865 | but it should work in the exam environment. 866 | 867 | #### CGAL Kernel Conversion 868 | 869 | See the [Cartesian_converter](https://doc.cgal.org/latest/Kernel_23/classCGAL_1_1Cartesian__converter.html) and the example. I'm not sure if it requires some special kernel field types as they are doing in the example or not. The [epec doc](https://doc.cgal.org/latest/Kernel_23/classCGAL_1_1Exact__predicates__exact__constructions__kernel.html) says that it uses cartesian representation however, so it probably works? 870 | 871 | ```c++ 872 | #include 873 | #include 874 | #include 875 | #include 876 | typedef CGAL::Simple_cartesian IK; 877 | typedef CGAL::Simple_cartesian > EK; 878 | typedef CGAL::Cartesian_converter IK_to_EK; 879 | typedef CGAL::Cartesian_converter EK_to_IK; 880 | ... 881 | EK_to_IK to_inexact; 882 | IK::Point_3 inexact_pt = to_inexact(exact_pt); 883 | ``` 884 | 885 | A different approach is [on github](https://github.com/CGAL/cgal/issues/1873#issuecomment-276306078) where they use it to convert from epec to epec_sqrt: 886 | 887 | ```c++ 888 | #include 889 | #include 890 | #include 891 | 892 | 893 | struct Lazy_gmpq_to_Expr_converter 894 | : public std::unary_function< CGAL::Lazy_exact_nt, CORE::Expr > 895 | { 896 | CORE::Expr 897 | operator()(const CGAL::Lazy_exact_nt &a) const 898 | { 899 | return ::CORE::BigRat(exact(a).mpq()); 900 | } 901 | }; 902 | 903 | CGAL::Cartesian_converter to_sqrt_kernel; 906 | 907 | int main() 908 | { 909 | CGAL::Epeck::FT a; 910 | CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt::FT b = to_sqrt_kernel(a); 911 | CGAL::Epeck::Point_3 c; 912 | CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt::Point_3 d = to_sqrt_kernel(c); 913 | } 914 | ``` 915 | 916 | > Note that I have hardcoded the number types of the Kernel (they might change if you do not use GMP). 917 | > Also notice that calling `exact()` on a lazy (nt or geometric object) will trigger the exact computation. 918 | > 919 | > [...] It really depends on what you are doing. About the trigger the exact computation, if you do not have a lot of cascaded constructions that should not be problem. 920 | 921 | #### k-th root and n-th power 922 | 923 | ![image-20210106101318755](README.assets/image-20210106101318755.png) 924 | 925 | https://doc.cgal.org/latest/Algebraic_foundations/classAlgebraicStructureTraits___1_1KthRoot.html 926 | 927 | #### Printing A Triangulation 928 | 929 | Search the [delaunay docs](https://doc.cgal.org/latest/Triangulation_2/classCGAL_1_1Triangulation__2.html#adf800f903a06c19d07d27afb2927fc71) for `i/o` and for the `<<` operator to see the relevant information. Basically, it first prints the number of vertices and edges (plus the infinite ones) and then the dimension - should usually be 2. Then the coordinates of all points. Then the faces and some more info. 930 | 931 | ## C++ 932 | 933 | Random selection of sometimes useful facts that I didn't know before (or maybe I did). Some of these, I don't recommend using because a simpler approach is often more code but less mistake-prone. Make your own decisions. 934 | 935 | Things like how to sort a `priority_queue` with a lambda function. 936 | 937 | ### Binomial Coefficient 938 | 939 | ```c++ 940 | #include 941 | # for computing n nCr k 942 | ``` 943 | 944 | ### Print Vector / Set 945 | 946 | ```c++ 947 | #include 948 | # Comma Separated output 949 | std::copy(path.begin(), path.end(), 950 | std::ostream_iterator(std::cout, ", ")); 951 | ``` 952 | 953 | ### IO Speed 954 | 955 | ```c++ 956 | std::ios_base::sync_with_stdio(false); 957 | ``` 958 | 959 | ### Initialize Vector of Vectors 960 | 961 | Use the [`std::vector::vector(count, value)`](http://en.cppreference.com/w/cpp/container/vector/vector) constructor that accepts an initial size and a default value: 962 | 963 | ```cpp 964 | std::vector > fog( 965 | A_NUMBER, 966 | std::vector(OTHER_NUMBER)); // Defaults to zero initial value 967 | ``` 968 | 969 | If a value other than zero, say `4` for example, was required to be the default then: 970 | 971 | ```cpp 972 | std::vector > fog( 973 | A_NUMBER, 974 | std::vector(OTHER_NUMBER, 4)); 975 | ``` 976 | 977 | See [my question](https://stackoverflow.com/questions/64004536/how-do-these-three-ways-of-creating-vectors-differ) for ways not to do it. 978 | 979 | ### Elementwise Sum of two vectors 980 | 981 | ``` 982 | #include 983 | #include 984 | 985 | template 986 | std::vector operator+(const std::vector& a, const std::vector& b) 987 | { 988 | assert(a.size() == b.size()); 989 | 990 | std::vector result; 991 | result.reserve(a.size()); 992 | 993 | std::transform(a.begin(), a.end(), b.begin(), 994 | std::back_inserter(result), std::plus()); 995 | return result; 996 | } 997 | ``` 998 | 999 | ### Max Element of Vector 1000 | 1001 | ```c++ 1002 | auto it = max_element(std::begin(cloud), std::end(cloud)); 1003 | myval value = *it; 1004 | ``` 1005 | 1006 | ### Read Rest Of Line 1007 | 1008 | https://stackoverflow.com/questions/63953373/how-to-ignore-the-rest-of-the-line 1009 | 1010 | ```#include 1011 | #include 1012 | cin.ignore(std::numeric_limits::max(), '\n'); 1013 | ``` 1014 | 1015 | ### Comparators 1016 | 1017 | If you have e.g. a `std::set`, it needs to know how to sort the contents. You can provide it with the function as a second template argument like I did in the following code snippet. 1018 | 1019 | Personally, I vastly prefer using lambda functions for this, although that adds some complexity. 1020 | 1021 | ```c++ 1022 | class Tracker { 1023 | private: 1024 | static bool cmp(Word a, Word b) { return a.word_id < b.word_id; } 1025 | public: 1026 | std::vector> sets; 1027 | std::vector interval_starts; 1028 | std::vector interval_ends; 1029 | std::vector interval_best; 1030 | uint n; 1031 | Tracker(uint n): n(n){ 1032 | // sets that are unique wrt to word id, not position 1033 | for (uint i=0; i(Tracker::cmp)); 1035 | interval_starts.push_back(0); 1036 | interval_ends.push_back(UINT_MAX); 1037 | interval_best.push_back(UINT_MAX); 1038 | } 1039 | } 1040 | ``` 1041 | 1042 | If you want to use a lambda function here, you would have to assign it to `std::function` instead of `auto` or alternatively pass it to the set's constructor as an argument: 1043 | 1044 | ```c++ 1045 | auto cmp = [](Word& a, Word& b) { return a.word_id < b.word_id; }; 1046 | std::set myset (cmp); 1047 | ``` 1048 | 1049 | ### Sort Vector of Pointers 1050 | 1051 | ```cpp 1052 | bool comparePtrToNode(Node* a, Node* b) { return (*a < *b); } 1053 | 1054 | std::sort(_children.begin(), _children.end(), comparePtrToNode); 1055 | ``` 1056 | 1057 | this can also be done with a lambda function. 1058 | 1059 | I like doing this when it would lead to mistakes if I were to move the original values around. E.g. when I need a list sorted in two different ways but still with the same modifiable objects. 1060 | 1061 | ### Hash Map of undefined hash key 1062 | 1063 | ```c++ 1064 | #include 1065 | #include 1066 | struct hasher_t { 1067 | std::size_t operator() (const std::pair &pair) const { 1068 | std::size_t seed = 0; 1069 | boost::hash_combine(seed, pair.first.x()); 1070 | boost::hash_combine(seed, pair.first.y()); 1071 | boost::hash_combine(seed, pair.second.x()); 1072 | boost::hash_combine(seed, pair.second.y()); 1073 | return seed; 1074 | } 1075 | }; 1076 | 1077 | class SQStorage{ 1078 | public: 1079 | std::unordered_map, ulong, struct hasher_t> sqdists; 1080 | SQStorage(){} 1081 | ulong squared_distance(P& a, P& b){ 1082 | std::pair key = std::minmax(a, b); 1083 | auto val = sqdists.find(key); 1084 | if (val != sqdists.end()) 1085 | return val->second; 1086 | 1087 | ulong value = CGAL::squared_distance(a, b); 1088 | sqdists.insert({key, value}); 1089 | return value; 1090 | } 1091 | }; 1092 | ``` 1093 | 1094 | sometimes you can also use `boost::hash>` directly. But only if the inner types are hashable. See also [here](https://www.boost.org/doc/libs/1_75_0/doc/html/hash/combine.html) 1095 | 1096 | ### How to Avoid Custom Hashes 1097 | 1098 | Just use a set of pointers instead, storing the values all in a vector somewhere. 1099 | 1100 | hashing for map/set customized (lambda, functor, pair) 1101 | https://stackoverflow.com/a/45395204/2550406 can I do pair hashing with lambdas? It seems like it's hard because you'd have to also specify the number of buckets, because the lambdas are not default-constructible. 1102 | 1103 | ### Priority Queue 1104 | 1105 | If you want to sort your own objects, you need the `decltype` of the specific lambda instance and also provide it in the queue constructor: 1106 | 1107 | ```c++ 1108 | auto edgecomp = []( MyEdge a, MyEdge b)->bool { return a.dist_sq < b.dist_sq; }; 1109 | std::priority_queue, decltype(edgecomp)> eq(edgecomp); 1110 | ``` 1111 | 1112 | Note that the queue provides access to the largest element on top, but the comparator is smaller-than. 1113 | 1114 | ### "Sort" but only nth element, O(n) 1115 | 1116 | [std::nth_element](https://en.cppreference.com/w/cpp/algorithm/nth_element) 1117 | 1118 | ### Multiset 1119 | 1120 | For a sorted (so easy to access minimum/maximum), const-time lookup (if an element exists) list. [multiset](https://en.cppreference.com/w/cpp/container/multiset) 1121 | 1122 | For removing a single element instead of all with the same value, do 1123 | 1124 | ```c++ 1125 | multiset.erase(multiset.find(2)) 1126 | ``` 1127 | 1128 | ### Linked List 1129 | 1130 | [std::list](https://en.cppreference.com/w/cpp/container/list) is a doubly linked list with `remove_if()`. Not sure how convenient it is to get the next item from a given item if you don't have an iterator though. 1131 | 1132 | ### Bitshifts 1133 | 1134 | Bitshifting [negative integers](https://stackoverflow.com/a/8416000/2550406) is undefined behaviour. So left-shifting will generally "append" a zero bit on the right, but right shifts only [sometimes](https://stackoverflow.com/q/8422424/2550406) "prepend" a one-bit on a signed negative integer. Also see [here](https://stackoverflow.com/q/7522346/2550406) 1135 | 1136 | ### Printing Doubles 1137 | 1138 | Usually, we don't need scientific notation. Often, they are actually integers. One option is to do 1139 | 1140 | ```c++ 1141 | #include 1142 | std::cout << std::fixed << std::setprecision(n); 1143 | ``` 1144 | 1145 | but we can also just cast it to long first and then print it normally. 1146 | 1147 | ### Binary Search 1148 | 1149 | Is not usefully built-in: it can search, but without returning the found item iterator. 1150 | 1151 | There is `std::lower_bound()` which finds an iterator to the first item in a list that is equal or larger to the given value. But in our algolab cases this is often not that useful because we compute the values on the fly. 1152 | 1153 | The multiset also has a `lower_bound` function. 1154 | 1155 | ### Recursive Lambda 1156 | 1157 | ![image-20210121210926663](README.assets/image-20210121210926663.png) 1158 | 1159 | ## Algolab-Infra 1160 | 1161 | ### Expert: Assertion Failure 1162 | 1163 | Code expert has assertion failure? maybe it's because you allocated a 2D-Matrix of `10^5 * 10^5`elements. I'm not certain but that might be because indexes to all those requires now a number larger than 32 bits. See https://stackoverflow.com/a/216266/2550406 - or maybe the expert is just out of memory. 1164 | 1165 | ### Expert: Assertion Failure 1166 | 1167 | Hand it in in the correct exercise :P 1168 | 1169 | ### Expert: Factor between score and wall time is too small. 1170 | 1171 | ![image-20210107155006785](README.assets/image-20210107155006785.png) 1172 | 1173 | That seems to happen when the exectuable (even just optionally) accepts command line inputs. Like this one: 1174 | 1175 | ![image-20210107155045253](README.assets/image-20210107155045253.png) 1176 | 1177 | Or when it prints too many debug prints. 1178 | 1179 | ### Expert: Audit Files 1180 | 1181 | > Please ignore the warning about the audit files, it does not affect your solution but only the logfiles. We will try to fix this soon. - Michael Hoffmann 1182 | 1183 | ## Algorithms 1184 | 1185 | Things to know by heart or at least well. You will have encountered them multiple time by the time the exam comes. 1186 | 1187 | ### Binary Search 1188 | 1189 | See [hand](#hand). Basic important things: 1190 | 1191 | * `left` and `right` are inclusive. 1192 | `while(left < right)` 1193 | * The end result is `left` and `right` but not necessarily `mid`! 1194 | * use `mid = left + (right - left)/2` to avoid overflows 1195 | * make sure there are no endless loops: If the mid assignment rounds down, it will choose left over and over again. So if the `left` case does not actually modify anything when `mid==left`, (i.e. it only does `left=mid`) then we need `mid = left + (right - left + 1)/2` instead. 1196 | 1197 | * write the simple structure first: 1198 | 1199 | ```c++ 1200 | while (left < right) // once this is done, left will equal right 1201 | { 1202 | mid = left + (right - left)/2; // avoid overflows 1203 | // Note: this rounds down. 1204 | value = compute(mid); 1205 | // think about what it means when value is bigger, equals, smaller. 1206 | if (value < SEARCHTERM){ 1207 | // increase index, it is too small 1208 | left = mid + 1; 1209 | } else { 1210 | right = mid; // mid could be it, or it could be smaller. 1211 | } 1212 | } 1213 | // left == right now. But maybe the last computation was a "not good" result. Easiest way: just compute it again. 1214 | value = compute(left); 1215 | ``` 1216 | 1217 | * think about the rounding this way: We don't want to endless-loop. 1218 | So if we had a remaining interval of size 2, and we chose the right one (in this example), nothing would change, that means we want to choose the other one if in doubt. This depends on in which case the `+ 1` lies. 1219 | If you want to choose the right case in doubt, use `mid = left + (right - left + 1)/2` instead, for rounding up. 1220 | 1221 | * Double-Check: is the value scaling with the index, or inversely? If the latter, maybe switch the cases? 1222 | 1223 | ### Sliding Windows 1224 | 1225 | See [Defensive Line](#defensive-line). 1226 | 1227 | While right is not at end, continually increase it. In each such iteration first move left as far right as you can to make the interval smaller. Then do a check, and then increase the interval to the right one. 1228 | 1229 | ```c++ 1230 | // initialize sum to vec[0], left to 0, right to 1 1231 | while (r <= vec.end()) // right exclusive 1232 | { 1233 | while (moving_left_is_possible // l < r-1 1234 | && moving_left_is_reasonable)// sum too big 1235 | { 1236 | currentSum -= vec[l]; 1237 | l++; 1238 | } 1239 | 1240 | if (currentSum == k) // If interval interesting 1241 | { 1242 | ranges.push_back({l, r}); 1243 | } 1244 | 1245 | if (moving_right_is_possible) // r < vec.end() 1246 | // it is now reasonable, because moving left 1247 | // is not possible now. 1248 | { 1249 | currentSum += vec[r]; // Advance right 1250 | r++; 1251 | } else { 1252 | // breaking is okay because we wrapped up left 1253 | // already. Thanks to right exclusive. 1254 | break; 1255 | } 1256 | } 1257 | ``` 1258 | 1259 | Right Exclusive also means that we can always first read the value and *then* increment `r/l`. 1260 | 1261 | Alternatively, only do one step in each loop and only have `if` statements inside it, that directly `continue` 1262 | 1263 | ### BFS 1264 | 1265 | Just keep a deque, add the first entry, and while it is not empty continue visiting the children that are not visited. 1266 | 1267 | Make sure to mark them as visited, and to check if there are no children / null children / infinite vertex. 1268 | 1269 | To check for connected components, a simple way is to do BFS once from each vertex, but only if not visited already. 1270 | 1271 | ### DFS 1272 | 1273 | Like BFS, but with a stack instead. That means when you throw children on it, and then the child's children, those will be handled first. 1274 | 1275 | Keep track of when you visit a node while going "downwards" so that you can pop it when it is encountered the second time, to go back up. 1276 | 1277 | ### Max Independent Set / Min Vertex Cover 1278 | 1279 | See advanced flow slides for how to code it. In rough: 1280 | 1281 | 1. Get a Maximum matching in the bipartite graph 1282 | 2. For building the Max Independent Set, choose from left side all that are unmatched, and all that are in the maximum matching but whose right-side pendant we did not take. 1283 | 3. For building the Max Independent Set, choose from right side all that are not directly connected to any leftside ones we have chosen. Because we want as many as we can. 1284 | 1285 | > ![image-20201225143237122](README.assets/image-20201225143237122.png) 1286 | 1287 | ### Cover All Cases 1288 | 1289 | Use a number in binary to represent every choice of taking or not taking an element - for fancy bruteforcing. 1290 | 1291 | Then generate all cases with 1292 | 1293 | ```c++ 1294 | for (int rep = 0; rep < (1<`, insert all relevant entries with value zero, then iterate over the keys and set the values to the `counter`. (Now never modify that map again.) 1312 | 1313 | This has runtime $\mathcal{O}(n\log(n))$ but that is probably okay if you're going to sort the intervals anyway. 1314 | 1315 | Make sure you don't overwrite your old coordinates if you still need them for score computation or something though. 1316 | 1317 | ## Github Repos 1318 | 1319 | Where you can find inspiration in code, when you're stuck. I have not often looked up things, and I could have saved so much time doing that. Because often your approach is entirely correct and your mistake is something very simple. 1320 | 1321 | * https://github.com/simon-hrabec/Algolab2020 (not all exercises, but with explanations) 1322 | * https://github.com/chaehni/AlgoLab (good topic summary, and some old exercise pdfs) 1323 | * https://github.com/JonasGessner/ETH-Algolab-2019 1324 | * https://github.com/YaweiYe29/AlgoLab-2017 1325 | * https://github.com/tehwalris/algolab 1326 | 1327 | ## Common Pitfalls 1328 | 1329 | ### Know The Algorithms 1330 | 1331 | Too often I have implemented one of them myself. Get an overview what boost has to offer. The slides list some algorithms worth knowing: 1332 | 1333 | ![image-20210121093107542](README.assets/image-20210121093107542.png) 1334 | 1335 | Also be aware of the tools at your disposal like the [Disjoint Sets for Clustering](#Disjoint-Sets--Cluster-Merging) I explained above, the `std::multiset` which stays sorted and allows for [fast access](How to use [lower bound on a set?](https://stackoverflow.com/a/10142015/2550406)) of the max and min. The boost flow algorithms. How to store properties with some boost Edge or Vertex or some CGAL Point. etc. 1336 | 1337 | ### Coding 1338 | 1339 | * Careful about indices. Sometimes the exercise sheet has indices counting from `1`! That matters for the output. 1340 | * LP: Make sure you didn't copy paste a wrong lower bound. 1341 | * LP: Make sure you specified the lower bound (if you want one) not just with the value but also with `true`! 1342 | * LP: No need to use the `int` type from the example you pasted. Use double, long, or something more exact. 1343 | * CGAL `to_double` is not exact! If everything looks right but the outputs are slightly wrong, maybe that's it. 1344 | * CGAL: if the output is of type double, and it is inexplicably wrong in the hidden tests, maybe it is negative zero at some point? A cast to `long` fixes that. 1345 | * CGAL: Is the input type correct? 1346 | * CGAL disables asserts! Re-enable them with `#undef NDEBUG` before importing `cassert` for the first time. 1347 | * If `make` gives you incredibly ugly output, chances are it is trying to compile a binary file. do a `make clean` before making it again. 1348 | * CGAL: Use the `cgal_create_cmake_file && cmake . && make` if you use CGAL! Otherwise you get ugly template errors. 1349 | * Flow: Do you need edges in the reverse direction? (It's advisable to use the edge adder class from the examples) 1350 | * Flow: Are you iterating over the reverse edges as well as the original edges and that is why it's wrong? 1351 | * Cost Flow: If you negated the cost so you can use nonnegative algorithms, did you convert it back correctly in the end? 1352 | * Cost Flow: again, if you negated the cost... you need to consider MAX_COST more than just once, otherwise you disincentivize taking many short edges over one long edge. 1353 | * Binary Search / Sliding Window: It pays off to do it consistently. Learn it by heart in a correct way, then always do it the same way. 1354 | * Sliding Window: Did you consider the end case? start case? what if left == right? 1355 | * Binary Search: Which way are you rounding? It should avoid endless loops in the case where you have only two elements left. 1356 | 1357 | ## Ideas For Exercises 1358 | 1359 | If you're stuck, there are many good repos out there with full solutions. If you either don't get them or you want to not see code yet and only the idea, check them out here. 1360 | 1361 | I didn't rephrase them much, these are just my notes. If they work for you, great. If they don't, sad. But I'm running out of motivation for this writeup. Sorry! 1362 | 1363 | ==**MAJOR SPOILER ALERT**== 1364 | 1365 | ### Even Pairs 1366 | 1367 | First compute for every interval $[0,i]$ how whether it is even or odd. That can be done iteratively in one pass. 1368 | 1369 | Simple Idea: Then compute for every interval $[i,j]$ whether it's even or odd by subtracting (even minus even is even, odd minus odd is even, otherwise odd). 1370 | 1371 | Better Idea: Do combinatorics instead. 1372 | choosing: $\left(\matrix{num\_even\\2}\right)+\left(\matrix{num\_odd\\2}\right)$ even pairs. 1373 | but that does always subtract the previous elements, especially always the first one. So we need to add the sums containing the first one manually as $+num\_even\_from\_zero$. 1374 | 1375 | ### Even Matrices 1376 | 1377 | If the question is "how many" but not which, we can take a shortcut by using combinatorics, like in [even pairs](#Even-Pairs). Then we know how to compute the score for submatrices of height 1 in $\mathcal{O}(n)$. 1378 | 1379 | Now we can compute for every fixed height submatrix the number of even matrices inside it that are of height as fixed. ( Reusing the sums of rows $i \text{ to } j$ for the rows $i \text{ to } j+1$ for some neat speedup. ) and then we can compute the generic solution of any height submatrices as 1380 | 1381 | $\mathtt{anyheight(from, to)} := \\ \qquad\mathtt{anyheight(from+1, to) + \\ \mathtt{anyheight(from, to-1) - \\ \mathtt{anyheight(from+1, to-1) + \\ \mathtt{fixedheight(from, to)}}}}$ 1382 | 1383 | The subtraction is because we counted some parts twice. 1384 | 1385 | The base case is simply the fixed height for height 1 rows. 1386 | 1387 | Use memoization for the remaining speedup. 1388 | 1389 | ### Search Snippets 1390 | 1391 | Use a vector to track in one element for each wordtype how many occurrences we have during the interval. Then when something changes, update only that entry and the total num of words if it goes to / above zero. 1392 | 1393 | Also, this is a nice function: 1394 | 1395 | ```c++ 1396 | if (std::all_of(times_word_is_in_interval.begin(), 1397 | times_word_is_in_interval.end(), 1398 | [](uint elem){return elem>0;})){ 1399 | ``` 1400 | 1401 | ### Ant 1402 | 1403 | Kruskal: the MST that is taking the closest tree to the current network (globally). 1404 | The second argument takes an output iterator. 1405 | 1406 | > The edges of the minimum spanning tree are output to this [Output Iterator](https://www.boost.org/sgi/stl/OutputIterator.html). 1407 | 1408 | ​ and this can be a vector! 1409 | 1410 | ```c++ 1411 | std::vector mst; // vector to store MST edges (not a property map!) 1412 | boost::kruskal_minimum_spanning_tree(G, std::back_inserter(mst)); 1413 | ``` 1414 | 1415 | Prim: uses dijkstra, gives the MST that is shortest paths from the root provided. 1416 | 1417 | If the MST is unique, they should be equivalent except that they have different runtimes and produce the MST in a different form. 1418 | 1419 | Then use all the different MST built with different property maps as edges in one big graph and do one last dijkstra. 1420 | 1421 | ### Real Estates 1422 | 1423 | * Careful if indices start at `1` in the input! 1424 | * Flow with cost: The "legal rules" are the flow. Inside them, we optimize the cost. Not the other way around. 1425 | 1426 | ### Maximize It 1427 | 1428 | General LP: 1429 | 1430 | * Make sure you didn't copy paste a wrong lower bound. Better to type it all by hand. 1431 | * Does the formula make sense? 1432 | * objective is minimized. If you want to maximize it and negate it, you need to change the sign of the result again. 1433 | * use `long`. 1434 | * `CGAL::to_double` can be used on `Quotients` like the solution objective_value. 1435 | 1436 | ### Tiles 1437 | 1438 | Think about simpler approach sooner than after 5 hours! 1439 | 1440 | Simple approach: 1441 | 1442 | ```c++ 1443 | /* 1444 | * 1. connect elements that *could* be connected 1445 | * 2. maximum cardinality matching 1446 | * 3. # matches == num_nonobstructed_elements ? 1447 | */ 1448 | ``` 1449 | 1450 | ### Diet 1451 | 1452 | LP: The equation number is the *second* argument to `lp.set_a` and the first is the variable! 1453 | 1454 | Remember that `CGAL::to_double` is inexact. So write a function (for `floor`) that first reduces the double until it is smaller or equal than the exact number, and then increase it as long as it stays smaller. 1455 | 1456 | ```c++ 1457 | double floor_to_double(CGAL::Quotient x){ 1458 | double a = std::floor(CGAL::to_double(x)); 1459 | while (a > x) a -= 1; 1460 | while (a+1 <= x) a+= 1; 1461 | return a; 1462 | } 1463 | ``` 1464 | 1465 | ### H1N1 1466 | 1467 | Idea is to build an MST with maximum width edges by negating all the edge weights. Then walk it for each query point. For building the MST, first build a graph where each face is a vertex except for all the infinite faces, which are all the same vertex, and then use that one as root. 1468 | 1469 | Need to check initial position as well. 1470 | 1471 | Copy from slides about how to store info inside the delaunay triangulation. it's worth the complexity. 1472 | 1473 | And remember that it's ok to use `t.locate(point)` for getting the face and still use `t.nearest_vertex(point)` for making sure the initial position is not too close to any. That's easier than working with the face for this as well. 1474 | 1475 | A Delaunay Triangulation face handle, vertex handle, edge handle can be dereferenced to get things. An `Edge` is explained [here](https://stackoverflow.com/questions/4837179/getting-a-vertex-handle-from-an-edge-iterator#comment115674370_4837696): 1476 | 1477 | > Dereferencing an `Edge_iterator` will give you an `Edge` according to the [documentation](http://www.cgal.org/Manual/latest/doc_html/cgal_manual/TDS_2_ref/Concept_TriangulationDataStructure_2.html#Cross_link_anchor_1307). 1478 | > 1479 | > ``` 1480 | > Edge` is definded as follows: `typedef std::pair Edge; 1481 | > ``` 1482 | > 1483 | > Dereferencing the `Face_handle` will give you a [Face](http://www.cgal.org/Manual/latest/doc_html/cgal_manual/TDS_2_ref/Concept_TriangulationDataStructure_2--Face.html#Cross_link_anchor_1309). 1484 | 1485 | So we can use `edge_handle->first` to get the face handle. And then `mirror_edge` with the edge ~~handle~~ to get the opposite view of the edge. and `.first` again for that face handle. 1486 | 1487 | * Prim gives us a distance map. That's like the edge weight map except that you can use the target vertex descriptor as key! So it's possible to walk the graph backwards and accessing the edges without building it. 1488 | 1489 | #### Similar trick to get the vertices of an edge 1490 | 1491 | https://stackoverflow.com/a/4837696/2550406 1492 | 1493 | ![image-20210110105029753](README.assets/image-20210110105029753.png) 1494 | 1495 | ```c++ 1496 | std::set reasonable_choices_for_s; reasonable_choices_for_s.insert(0); 1497 | for (auto e : boost::make_iterator_range(tri.finite_edges_begin(), tri.finite_edges_end())){ 1498 | Triangulation::Face& face = *(e.first); 1499 | Vertex_handle v1 = face.vertex(face.cw(e.second)); 1500 | Vertex_handle v2 = face.vertex(face.ccw(e.second)); 1501 | reasonable_choices_for_s.insert( 1502 | storage.squared_distance(v1->point(), v2->point(), v1->info().id, v2->info().id)); 1503 | } 1504 | ``` 1505 | 1506 | ### Bistro 1507 | 1508 | Use `vertexhandle->point()` to get a `K::Point_2` from a delaunay vertex handle. Note the arrow! 1509 | 1510 | ### San Francisco 1511 | 1512 | `uint` up to $2^{32}$, `unsigned long` up to $2^{64}$. 1513 | 1514 | I never finished this exercise. 1515 | 1516 | ### Radiation 1517 | 1518 | * CGAL disables asserts. Reenable them with `#undef NDEBUG` before the import of ``. 1519 | * Use correct input type! And generally, use double instead of int, for speedup that I don't understand but is backed by the docs. 1520 | * Use 1521 | 1522 | ```c++ 1523 | // set solution strategy to avoid getting stuck 1524 | CGAL::Quadratic_program_options options; 1525 | options.set_pricing_strategy(CGAL::QP_BLAND); 1526 | Solution s = CGAL::solve_linear_program(lp, ET(), options); 1527 | ``` 1528 | 1529 | if it endlessly loops. 1530 | 1531 | * **Always** set lower bounds *with* a boolean (or upper bounds): `lp.set_l(X, true, 2)`. 1532 | 1533 | * Do the binary search right! (Or exponential search, if the smaller checks are fast and the big checks are slow. To safe time by not starting in the middle.) 1534 | 1535 | * Think about these edge cases in the exponential search: 1536 | 1537 | * zero works already 1538 | * smaller than maximum but larger than previous exponential check 1539 | * larger than maximum -> fail 1540 | * stepping from zero to one 1541 | 1542 | It's probably easiest to never stop early in the exponential search - just use it to find a good right bound for the binary search. 1543 | 1544 | ### Asterix The Gaul 1545 | 1546 | The non-recursive iteration is done with `for(uint combi=0; combi (Pathvertex1)--[min]-->(Sink) 1556 | | max - min | 1557 | (Source)--[min]-->(Pathvertex2) 1558 | \-----[max]----->(City2) 1559 | ``` 1560 | 1561 | By adding a cost of 1 to the `| max - min |` path and a cost of 0 to all other, I incentivize taking the minimum first. 1562 | 1563 | **But** I get time limits exceeded. 1564 | 1565 | > In the integer capacity case, if *U* is the value of the max flow, then the complexity is *O(U \* (|E| + |V|\*log|V|))*, where *O(|E| + |V|\*log|V|)* is the complexity of the dijkstra algorithm and *U* is upper bound on number of iteration. In many real world cases number of iterations is much smaller than *U*. 1566 | 1567 | See continuation a bit further down for how this really works. 1568 | 1569 | ### Kingdom 1570 | 1571 | Think about a minimum flow per edge as an additional consuming edge to the sink and another edge that gives that back from the source. Since we use the max flow to enforce a MAXIMUM flow to the sink (i.e. all sink-paths from cities MUST be taken) we can add these additional sink paths for the edges as just as binding constraints. Only if the max flow saturates all edges to the sink is it okay. 1572 | 1573 | The basic idea is this: 1574 | 1575 | ```c++ 1576 | (City1)--[max]--> (Pathvertex1)--[min]-->(Sink) 1577 | | max - min | 1578 | (Source)--[min]-->(Pathvertex2) 1579 | \-----[max]----->(City2) 1580 | ``` 1581 | 1582 | The takeaway is that no cost for preference is required because we need to fill them all anyway. Cost takes too long because it scales with the value of the max flow. 1583 | 1584 | *More Generic Phrasing:* 1585 | If we want to tell whether a flow is possible so that each edge has a minimum usage and each vertex has some original budget and some final requirement, we can model it as follows and then check whether the flow is maximal. We *need* to fill all connections to the sink - they stand for the edge minima. But now we've lost some of our flow, so we need to give it back at the edge destination with a connection from the source. (`max - min`). 1586 | Because we have sink connections also in the edges, the edges are forced to be taken, and so it is not a problem that maybe the additional sources fill the graph without taking enough edges. 1587 | 1588 | ### Important Bridges 1589 | 1590 | Biconnected Components: 1591 | 1592 | ```c++ 1593 | #include 1594 | std::vector compmap(mbridges); 1595 | long nbc = boost::biconnected_components(OG, boost::make_iterator_property_map(compmap.begin(), boost::get(boost::edge_index, OG))); 1596 | 1597 | ``` 1598 | 1599 | A biconnected component is one where you can't remove a single vertex to disconnect it. We are looking for the edges that are alone inside a biconnected component. 1600 | 1601 | Because if there were more edges in the component, then removing one would not matter - since it is strictly better than removing one whole vertex, and even that would be okay. 1602 | 1603 | If there is only one edge, there are only two vertices in the component - and removing the edge would disconnect the two. Because if there was a different way, it would also be in the connected component. 1604 | 1605 | ### Hiking Maps 1606 | 1607 | * If you get ugly output in `make`, do `make clean` and then retry. 1608 | * If something is broken, verify your constants! E.g. three times checking on the same line instead of three different lines is bad. 1609 | * Are start and end inclusive or exclusive? are their initial values consistent? and their abort conditions? 1610 | * Basic idea: sliding window but with a vector of "sum int" values carried along so that counting which variable is how often in the interval does not require $n$ runtime every time. 1611 | * Using a known point on one side of the line and `CGAL::oriented_side` to compare sides. It's either, boundary or one or the oder side. 1612 | 1613 | ### Casino Royale 1614 | 1615 | * if assigning something to something else does not work, maybe switch them. As here: the Agents are not the flow - the available places in the train flow, and collect agents. 1616 | 1617 | * Negating Flow Cost: `MAX - priority` does not work if it's about the sum of edges with different lengths. E.g. `(MAX-30) + (MAX-54) + (MAX-69)=234` and `MAX-128=1` so the latter has a smaller cost but would not be the better choice. 1618 | The solution: `MAX*edgelength - priority` to add the superfluous maximum to the longer edge as well, for comparison. 1619 | * Instead of weird computations I struggle with, I can convert it also back by going over the used edges (e.g. store all edge descriptors that represent relevant missions) and checking whether they have flow (capacity minus residual_capacity > 0). 1620 | * When going over the graph, make sure to not consider the backwards going edges. E.g. by storing a boolean in the edge data when inserting them with the edge_adder. 1621 | 1622 | ### Chariot Race 1623 | 1624 | * If you assign to a reference, you don't change the reference - you change the real object! 1625 | * Random Segfault? Might be stack overflow due to long recursion. 1626 | * Writing down algo first and working through it on paper is actually nice. 1627 | * Not using boost gave me a speedup of 4 whole seconds! It took 22 seconds with boost and 18 with my custom structure. So a speedup of 18%. 1628 | * Basic Idea: it's a tree and each subtree has three cases. So we do this building up from the leafs. The cases are "assuming root is taken", "assuming parent of root is taken and root is not", and "no assumptions about parent of root, and root is not". 1629 | The reason we need three cases is that the parent being taken influences the subtree. 1630 | 1631 | ### Defensive Line 1632 | 1633 | * Always remember: if you're using a boolean flag `initialized`, you need to set it to `true` once it is. 1634 | 1635 | * Better solution than I have is [here](https://github.com/YaweiYe29/AlgoLab-2017/blob/master/week11/exercises/defensive_line.cpp#L25) and uses a DP table. Thinking for each attacker, if it had to choose in the range up to `j`, which one would it choose? 1636 | For some `j` it would take the previous defender-block at `j-1`, for others it might take the `j` th but hence the previous attacker can only take the best option in the range `j-len`, and in others it might have no good choice. 1637 | So every attacker builds upon the previous one, which was computed for all positions, and then we read the total sum out from the last entry. 1638 | If there was a "no good choice" somewhere, in a path the final attacker actually relies on, then that has happened in the last agent because each one chose optimally. So if the last and the second last agent have the same score for the full range, we say `fail`. 1639 | 1640 | * In other words: We want to know for an interval `[0, dj]` with a number of attackers `a` the maximum possible attacked defenders (according to the rules). 1641 | We have to make sure all attackers are involved, but that can be checked in the end by looking if the best result with `a` attackers is the same as with `a - 1` attackers. ( ==Okay, no, that's actually wrong I think== but it passes all their tests. e.g. 3 1 1 1 1 3 with k=4 has score=4 for both a=1 and a=2.) 1642 | The maximum with `a` attackers can be computed by considering what the best option would be: Either to take an interval ending at `dj` with the last attacker and hence only taking the optimum of the free interval before that with `a - 1` attackers or not taking an interval ending at the position `dj` and hence doing either nothing or taking the optimum for the interval up to `dj -1`. 1643 | 1644 | * To fix that "it's actually wrong", we need to store how many attackers we used in each step. 1645 | 1646 | * Elegant Sliding Window implementation at [gihtub](https://github.com/JonasGessner/ETH-Algolab-2019/blob/master/defensive_line.cpp#L82) 1647 | 1648 | ```c++ 1649 | while (r <= n) 1650 | { 1651 | while (currentSum > k) // Keep up with left 1652 | { 1653 | currentSum -= values.at(l); 1654 | l++; 1655 | } 1656 | 1657 | if (currentSum == k) // If left and right match 1658 | { 1659 | ranges.push_back({l, r}); 1660 | } 1661 | 1662 | if (r < n) 1663 | { 1664 | int atR = values.at(r); // Advance right 1665 | currentSum += atR; 1666 | } 1667 | r++; 1668 | } 1669 | ``` 1670 | 1671 | and another one [here](https://github.com/YaweiYe29/AlgoLab-2017/blob/master/week11/exercises/defensive_line.cpp#L25): 1672 | 1673 | ```c++ 1674 | while(head < n){ 1675 | int diff = defence_sum[head] - defence_sum[tail] + defence[tail]; 1676 | if(diff == k){ 1677 | blocks[head] = head - tail + 1; 1678 | head++; tail++; 1679 | } else if (diff < k){ 1680 | head++; 1681 | } else { 1682 | tail++; 1683 | } 1684 | } 1685 | ``` 1686 | 1687 | * No need to store a vector of all intervals that end at a position... because there can be at most one such interval of the valid sum - we don't have negative numbers. 1688 | 1689 | * sliding windows: ALWAYS update the sum, not just the left/right variable 1690 | 1691 | #### Defensive Line vs Casino Royale 1692 | 1693 | In Casino Royale we had missions that should not overlap (if we have space for only one agent in the train). In Defensive Line we have attacker intervals that should not overlap. So it might seem like we can code Defensive Line with Casino Royale's approach. 1694 | 1695 | To model that, (it probably won't work, but here are thoughts on it), we have a flow of $1$ that shall flow through the line of defenders. At any point, it may either go to the next defender or take the direct edge to the end of the interval of sum $k$. 1696 | So while the flow is taking an interval, we are sure no second interval can be overlapping - except for at the defender where the interval ends, maybe? 1697 | 1698 | To ensure that the end of an interval is not already taken again as the start of the next interval, we model each defender as two vertices: One $D_{in}$ and one $D_{out}$. The interval edges start at $D_{in}$ and end at some future $D_{out}$. 1699 | 1700 | We also have the constraint that every attacker must attack some interval. That means in our model that we need to have $a$ interval-edges taken. This is a difference to Casino Royale where we want to fully maximize the profit of missions (think of attacked intervals as missions, with profit equal to number of defenders in it) (profitable missions are incentivized by *cost*). 1701 | In other words: The main difference between Defensive Line and Casino Royale is that in Casino Royale *If two intervals don't overlap, we can take both* and in Defensive Line *If two intervals don't overlap, we can only take both if we still have a spare attacker*. 1702 | 1703 | We can run a flow and then check whether the number of taken interval-edges is equal to the number $a$ of attackers we have available. If it is equal, we're done. Otherwise, I don't really know how to deal with it? 1704 | 1705 | We could perhaps use a multi-layered graph approach. Every time an interval-edge is taken, it leads the flow to the next layer. After $a$ transitions, there are no more interval-edges available so the flow has to walk the remaining defender line to the sink without choosing any new intervals. 1706 | 1707 | So that could actually work, unless somebody spots a flaw?. However, the runtime complexity is not great with num_vertices = num_attackers * num_defendants. Successive Shortest Paths has Runtime Complexity $O(|E| + |V|*log|V|) $so we would end up with $O(possible\_intervals + num\_attackers*num\_defendants*log(num\_attackers*num\_defendants).$ 1708 | 1709 | > Why can't we simply take the $a$ of attackers who cover the most defenders in it (the $a$ top profitable missions in the flow)? 1710 | 1711 | 1712 | I think you might be right. 1713 | 1714 | Running a mincost maxflow algo on the graph gives us some chosen intervals so that their sum of defendants is as large as can be. If there are exactly $a$ then we're happy. If there are more, we can choose the most profitable intervals that have flow, as you are suggesting. 1715 | 1716 | But could there also be less than $a$ chosen intervals although it would be possible to choose more, at a higher cost? 1717 | We need to have every attacker attack a nonzero interval. If the best choice in terms of number of defendants covered would be to take one long interval, the flow will take that one. But sometimes it should not. I'll try attach an example picture. 1718 | 1719 | Say we have attackers of strength 6, and defenders of strengths 5, 1, 1, 1, 1, 1, 1, 5. Then the best choice without the constraint that an attacker can only take one interval (Casino Royale Style) would be to take the interval with all the values 1, giving a total of six covered defenders. 1720 | But if we have two attackers that both *must* attack, then the best choice would be to take the first two defenders and the last two defenders, resulting in a total of four. Which is less, so the flow won't come up with it. 1721 | 1722 | ![Anhang defline.png](README.assets/defline.png) 1723 | 1724 | ### Attack Of The Clones 1725 | I failed this task 13 times, even with master solution open during at least nine of the retries. A year later, I had another go at it but this time without undefined behaviour. If you want to see the generic idea or my rust code for it, see [attack of the rusty clones](https://github.com/lucidBrot/attack-of-the-rusty-clones). 1726 | 1727 | ### World Cup 1728 | 1729 | Basic idea: do LP with the constraints first, maximizing profit. One limit for supply in each warehouse, one constraint for sum of incoming beer per stadium, and one constraint per stadium that the sum of incoming alcohol times its percentage must be less or equal to the allowed liters. In those constraints, the contour lines cause some reduced revenue per connection. 1730 | Speedup: use delaunay to filter out the contour lines that have no point in range. 1731 | 1732 | **rounding down to double**: changed parameter type of the example the algolab people give in `hello_exact.cpp`: 1733 | 1734 | ```c++ 1735 | double floor_to_double(const CGAL::Quotient x) 1736 | { 1737 | double a = std::floor(CGAL::to_double(x)); 1738 | while (a > x) a -= 1; 1739 | while (a+1 <= x) a += 1; 1740 | return a; 1741 | } 1742 | ``` 1743 | 1744 | **Locally Passing, but Wrong Answer online**: 1745 | 1746 | ```c++ 1747 | std::cout << (long)(floor_to_double(-s.objective_value())); 1748 | ``` 1749 | 1750 | The cast to `(long)` somehow helps. Maybe it got rid of negative zero for me. 1751 | 1752 | **Test if circle is empty** by using delaunay triangulation of all points that could be inside and test which of them is the nearest and whether it is inside. We are told that there aren't many circles with anything inside, so this should be fast. 1753 | 1754 | **Always check your input type** 1755 | 1756 | ![image-20210106133741713](README.assets/image-20210106133741713.png) 1757 | 1758 | my LP input type was defined as `int`, not `double`, so the bright version worked and the dim version did not work. 1759 | 1760 | ### New York 1761 | 1762 | * Do DP with table building it up instead of trying to be smart and somehow subtracting from the prefix sum. That does not work for finding minima/maxima when sliding. 1763 | * When you first had a DP table and then reduce that to two vectors, be very careful to read from the right one and write to the right one: In the first run after the initialization, it's probably different than after that - but only for reading. 1764 | * When you think it's sliding window or greedy, maybe it's DP! 1765 | * But maybe it's actually not fast enough with DP! 1766 | * I ended up with 60% sliding window and 100% doing it recursively. For the recursive bfs, make sure to use a reference instead of copying the parameters every time, and restore the old state of them before returning. 1767 | * I guess the problem with the sliding window is that I moved from the leaves upwards and hence re-inserted some temperatures multiple times when there were intersections. The DFS approach comes from the other side and does not recompute those. 1768 | 1769 | ### Lestrade 1770 | 1771 | * Careful when shuffling things. Are there really no relations to other vectors? 1772 | 1773 | * Storing information inside Delaunay *Vertices* requires the import differently than in the tutorial slides pdf: 1774 | ![image-20210108113435616](README.assets/image-20210108113435616.png) 1775 | 1776 | ```c++ 1777 | #include 1778 | #include 1779 | #include 1780 | #include 1781 | typedef CGAL::Exact_predicates_inexact_constructions_kernel K; 1782 | typedef CGAL::Triangulation_vertex_base_with_info_2 Vb; 1783 | typedef CGAL::Triangulation_face_base_2 Fb; 1784 | typedef CGAL::Triangulation_data_structure_2 Tds; 1785 | typedef CGAL::Delaunay_triangulation_2 Triangulation; 1786 | typedef Triangulation::Face_handle Face_handle; 1787 | typedef Triangulation::Vertex_handle Vertex_handle; 1788 | typedef Triangulation::Point Point; 1789 | ``` 1790 | 1791 | * There is an insert function that accepts a vector of pairs! [It](https://doc.cgal.org/latest/Triangulation_2/classCGAL_1_1Delaunay__triangulation__2.html#a0ea76bda5cb948080eebf974af7fc506) gave me the last 30% of the points!! 1792 | 1793 | * `tri.locate(Point p)` returns a face handle. `nearest_vertex` a vertex handle. 1794 | 1795 | * Comparisons with ints and exact types in LP: `s.objective_value() > ET((long unsigned int)z)` 1796 | 1797 | * Delaunay: build in O(b * log b) , query in O(log b) wenn b d azahl pünkt i de triangulation isch. D.h. q mal querie isch O(q log b). Und das heisst es isch actually besser wenn mer chliners b als q hät wenn mer cha wähle 1798 | bi noni 100% sicher aber... also sägemer du hesch zwei zahle `a << b`. Denn isch wenn s set `A` zum builde bruchsch und nacher für jede im set `B` queriesch d total runtime `O(a log(a) + b log(a))`. Umgekehrt wärs `O(b log(b) + a log(b))`. D frag isch also ob `log(a)` oder `log(b)` grösser isch. mit me chlinere set pünkt und me grössere set queries isch also besser. Stimmt die logik? 1799 | 1800 | * Skip those agents that are nearest to the same gang member but cost more. 1801 | 1802 | * ```c++ 1803 | /* 1804 | * Delaunay on the network of gang members to find for each agent the corresponding gang member. 1805 | * Then LP with the constraints of size agents hours plus an equation requiring to be below 1806 | * the cost of holmes. 1807 | * If not feasible, say Holmes, else Lestrade. 1808 | * 1809 | * There are quite many gang members. Luckily they aren't relevant for the LP part. 1810 | * 4k agents was already possible once for LP, so why not again. 1811 | */ 1812 | ``` 1813 | 1814 | ### Clues 1815 | 1816 | * use `sync_with_stdio(false)` to get the last test without TLE. 1817 | 1818 | If there is a 2-coloring in the graph, there is also one in the delaunay triangulation (in both, we only consider the connections between points that are close enough). 1819 | 1820 | That does not mean that a valid 2-coloring in the delaunay graph implies a valid 2-coloring in the real graph, but it allows us to abort early. 1821 | 1822 | If we did not abort early, only the points in the same connected component are relevant - all other points are far away anyway. And **the connected components of the delaunay graph and of the real graph are the same**. Because the delaunay graph contains the nearest neighbours. 1823 | 1824 | To check for the connection inside the same color set, we build another delaunay triangulation. That's the secret trick! Then we can easily look at the closest neighbour distance for each in the set in less than $\mathcal{O}(n^2)$. The rhombus fact below is not useful for this in any way. 1825 | 1826 | #### Rhombus Fact 1827 | 1828 | | short diag | long diag | 1829 | | ------------------------------------------------------------ | ------------------------------------------------------------ | 1830 | | ![image-20210108112354639](README.assets/image-20210108112354639.png) | ![image-20210108112400538](README.assets/image-20210108112400538.png) | 1831 | 1832 | Circumcircle must be empty, so the edge case where a point is completely hidden can happen but the diagonal will always be the shorter one - so it can't be hidden over connections that are all longer than we allow if the hidden point is close. 1833 | 1834 | #### Early Aborts 1835 | 1836 | * Make sure you're reading all of the inputs 1837 | * If you check for visited / not visited in BFS, think about the case where a vertex is already visited (and hence would be skipped) before killing the whole project. 1838 | 1839 | ### Hand 1840 | 1841 | Basic Idea: Delaunay Triangulation of tents and then do BFS on it with a given distance. That finds the number of families. Then do that multiple times with binary search to answer the reverse question. Finally, note that $k\leq4$ so merging remaining clusters that are too small is not hard. 1842 | 1843 | Alternatively, [Simon Hrabec](https://github.com/simon-hrabec/Algolab2020/tree/main/Week%2011%20-%20Hand) found a solution that does not require binary search. This is probably what I missed for the last 20%. He sorts the delaunay edges by distance and iterates through the distances, merging clusters of tents that *must* be together until done. 1844 | 1845 | * Refresh how to do bfs on a delaunay graph. Best look at the code in ex11/hand again. 1846 | 1847 | * Remember to skip infinite vertices. 1848 | 1849 | * increment circulators even when using `continue`, not just in the normal case! 1850 | 1851 | * Remember to check if the circulator is empty (equal to `NULL`) 1852 | 1853 | * If the triangulation does weird stuff - like giving you a NULL circulator - better make sure you read the points in correctly! 1854 | 1855 | * Simple Binary Search. Learn by heart! Except that this paste here is wrong. Use the `ex11/hand` one. 1856 | 1857 | ```c++ 1858 | ulong left = 0; ulong right = (1UL<<50)+1; //both inclusive 1859 | ulong mid = 0; 1860 | while (left < right){ 1861 | mid = left + (right - left)/2; 1862 | ulong f = f_for_s0(mid, tri, k, n); 1863 | if (f < f0){ 1864 | // that's too small 1865 | left = mid+1; 1866 | } else { 1867 | // f >= f0 1868 | // that's big enough, maybe too big 1869 | right = mid; 1870 | } 1871 | } 1872 | // now left == right. But maybe the last check was actually on the "wrong" side. So don't use mid here. 1873 | ulong biggest_s = right; 1874 | ``` 1875 | 1876 | * one mistake I made is because the `f` is smaller for larger inputs `s`. So I had to *decrease* the searched-for input to make`f` larger. 1877 | * another one is the rounding in the computation of `mid`. 1878 | Usually, we would use `mid = left + (right-left)/2` to avoid overflows. 1879 | Now, however, I want a round up in the case `[enough_f, not_enough_f]` because in the round down case it would leave `left` equal and in the round up case it reduces `right` by `1`. That difference became a problem because I had to switch when left and when right were assigned, due to the previous point. 1880 | 1881 | * ![image-20210110083909495](README.assets/image-20210110083909495.png) 1882 | 1883 | ### Moving Books 1884 | 1885 | * use the std::multiset. remove only one of the multiple elements with: 1886 | 1887 | ```c++ 1888 | std::multiset::iterator hit(mySet.find(5)); 1889 | if (hit!= mySet.end()) mySet.erase(hit); 1890 | ``` 1891 | 1892 | * The trick from the solutions is to think about what happens in one round, and loop over that. Using sorted boxes and sorted people, we can skip the weak ones. In total, that sums up to only num_boxes. 1893 | 1894 | * I had sorted both lists - all I was missing was the multiset instead of manually doing binary search 1895 | 1896 | ```c++ 1897 | auto boxsort = [](uint a, uint b){return a > b;}; 1898 | std::multiset boxes {boxsort}; 1899 | //... 1900 | // carry box as heavy as I can 1901 | // "lower_bound" gets me the first element that is the key or later (smaller) 1902 | auto boxit = boxes.lower_bound(f.str); 1903 | ``` 1904 | 1905 | * alternative idea: test if it is possible to do in `r` rounds and then do binary search. Reasonable if the test is fast. 1906 | 1907 | * Estimate based on numbers how runtimey I need to be! 1908 | 1909 | * With multiset, can specify the comparator just like with priority_queue (except there is no middle template type). The set is ordered, so `.begin()` and `.rbegin()` access the smallest/largest entry. 1910 | 1911 | * Multiset has its own `lower_bound()` function. It returns the first key that is equal or later in the multiset. This is based on the provided comparator. 1912 | 1913 | * Multiset comparator should take either copies or `const` references. 1914 | 1915 | ### Hong Kong 1916 | 1917 | * You can use `tri.segment(facehandle, i).squared_length()`. instead of 1918 | 1919 | ```c++ 1920 | num_t edgelen = CGAL::squared_distance( 1921 | fh->vertex(fh->cw(i))->point(), 1922 | fh->vertex(fh->ccw(i))->point()); 1923 | ``` 1924 | 1925 | * Using a priority queue with faces sorted descending by their newest value means we don't propagate some of the values that would just be overwritten again soon: Starting with the largest escapable value and propagating it, then continuing sorted by that, means that the largest value must be the first one a face gets assigned. So all others can be dropped! Compute this way the largest escapable value per face, then query the balloons unconstrained. 1926 | 1927 | * Is this easier than the maxwidth MST approach in [H1N1](#h1n1)? No, but it's more appropriate because we have multiple goals to reach. 1928 | 1929 | * Accept the first update always! Otherwise you might not propagate your initial state correctly. 1930 | 1931 | * Given a face, there is no way to get the edge handle directly. But why would you want that? The info for the vertices, you have. 1932 | 1933 | * Think about why they provided a footnote. It's maybe not just to mislead you. 1934 | 1935 | * You can use `facehandle->neighbor(i)` as per [docs](https://algolab.inf.ethz.ch/doc/cgal/doc_html/TDS_2/classTriangulationDataStructure__2_1_1Face.html#a049f6aad0b7303c8a9a6051857148355) 1936 | 1937 | * Save maximum freeable in each face, bottom-up. No idea about the runtime, it takes a while to propagate. Just do it. 1938 | 1939 | * Same size needed for going through an edge when comparing squared, as for total takeoff inside a face: Both require $(2(s+r))^2$. so can store both as one tracker. 1940 | 1941 | * ```c++ 1942 | /* Redo for exam prep. 1943 | * 1944 | * Triangles with balloons inside. Output yes or no whether the balloon can launch. 1945 | * Give each balloon an id. 1946 | * 1947 | * Check if balloon can exist at its starting position. if no, the answer is no. 1948 | * Existence: distance to the closest tree is larger or equal to balloonradius+treeradius 1949 | * 1950 | * Then check if balloon can launch at that position, because that's the first test case. 1951 | * Launch at Position: Distance to the closest tree is larger or equal to 2(balloonradius+tree) 1952 | * 1953 | * Create a sorted queue containing Faces to consider and the largest value currently in the queue 1954 | * they should receive. We avoid useless propagations by always taking the largest. 1955 | * This way, propagate to each face the maximum balloon radius that can still escape somehow. 1956 | * 1957 | * Now for each balloon that can exist, locate the face it is in and check if it can escape. 1958 | * 1959 | * To compute for a face how large balloons it lets launch, the answer while the circumcenter is 1960 | * inside the triangle is the distance circumcenter<->corner. 1961 | * If the circumcenter is outside, the width available grows larger as you move from the opposite 1962 | * corner through the triangle and outside towards the circumcenter. Any balloon that entered the 1963 | * triangle from outside is large enough to pass through that edge because it is the largest edge. 1964 | * And balloons who spawned inside the triangle and can exist have enough distance to reach the 1965 | * circumcenter. 1966 | */ 1967 | ``` 1968 | 1969 | 1970 | 1971 | * Recall that if you have $(r+s)^2$ already computed, and you want to compute $(2(r+s))^2$, you can do $\mathbf{4} \cdot (r+s)^2$ 1972 | 1973 | * There is apparently some speedup from using `emplace` instead of `push` which is required for this exercise?! Either that or the usage of `VertexInfo` in the triangulation... but I've tested it now and the difference is the emplace. 1974 | 1975 | * TLE: because I added all faces multiple times at the start. That's not too big a difference in terms of runtime, but made the queue too long. So instead only insert the finite faces once and for the infinite faces the neighbours (or themselves once. whatever the approach needs.) 1976 | 1977 | * Alternative against TLE: keep a count of the number of optimal triangles. Modify it when a triangle is added ot the queue and when it is removed from the queue. When all are optimal, stop. 1978 | 1979 | ### Motorcycles 1980 | 1981 | * It seems like using an inexact kernel first, and an exact kernel only if there is an intersection, is not a great speedup. 1982 | * Avoid dividing by zero :P 1983 | * If the question is only "yes" or "no", perhaps we can make general decisions. Like "any biker slower than the fastest either reaches it too late and dies, does not reach it because it died earlier, or is moving in the other direction. Since the last one can be assumed not to be the case (test case restriction) and the other two have the same end result, we just store that it failed". 1984 | * The basic idea was to sort the bikers by speed in x direction. The fastest biker will always do it. The second fastest only if it is not blocked by one of the faster bikers. Etc. Assuming they all move upwards, we only need to store the limiting biker. A new biker starting above the topmost speedy biker will survive, a new biker starting below it will not survive. And then extend to the general case: what if some of them move downwards? 1985 | In that case, they wouldn't be blocked by the upwards-moving limiting biker above them, but below them. Or by a downwards-moving limiting biker below them. (not a downwards moving biker above them, because that one has flatter slope than the currently considered biker). 1986 | 1987 | ### Punch 1988 | 1989 | * LP: no way to do integer linear programming during exam. The libraries for it are probably not available. See [here](https://doc.cgal.org/latest/Solver_interface/Solver_interface_2mixed_integer_program_8cpp-example.html). 1990 | Using input type `CGAL::Gmpz` does not seem to make the variables integral, it's only about the types used for input (and the "exact type" is just always `Gmpz` except when the input type is `Gmpq`). 1991 | * DP: If the table starts at 0, maybe need `k+1` instead of `k` width?! 1992 | * Basic DP Idea: Store total cost given volume taken of i-th beverage and all before. So the table is num-beverages rows and num-volume-taken columns. In the end read out the cheapest cost (or if there are multiple the best variety one.) So in each entry store the cost and the variety. 1993 | Table entries are computed based on what would be better - either taking the best of this volume without the latest drink, or taking this drink at least once and adding the best of this drinks' row for the smaller volume. 1994 | * In other words (after solving again): 1995 | Each kind of drink is either taken or not, for a given volume. If it is taken, look at the same drink for less volume to be achieved (plus the cost of that bottle), if it is not taken, look at the same volume with one less drink to choose from. Always choose the best of these options. 1996 | 1997 | ### Evolution 1998 | 1999 | * When sorting with a priority queue, remember that the sorting lambda is inversed. Because the queue always gives you the *largest* element and the lambda returns `true` if it is *smaller*. 2000 | * If results are wrong and you have loops, check if some loops should be if, or if some if statements should be loops. Easiest check with a simple test case to work through and enough debug prints. 2001 | * Using DFS, we can incrementally compute all `(start, end)` paths instead of having to do $n^2$ comparisons! 2002 | * Binary search on the tree paths for speedup. 2003 | * The solution DFS approach works this way: Use DFS to store the path for each node up to the root explicitly at the node. Then we can do binary search on the stored path to find the oldert still okay species. 2004 | * In order to use less memory, they handle all queries that start at that node while doing the bfs, hence not having to store the path for longer. 2005 | 2006 | ### inball 2007 | 2008 | * Remember that the objective value is *minimized*. 2009 | * Think about it in more geometric than algebraic terms? 2010 | * If, for some reason, you split up x and y into $x^2$, $xy$, $y^2$, you will have to check whether the x are the same. can't enforce that. 2011 | * Square / lift the given values, not the variables. 2012 | * It might help to think about what exactly actually is unknown. In this case the center and radius. 2013 | * Write everything anew when you change the idea! too much to forget changing! 2014 | 2015 | ### Marathon 2016 | 2017 | * Read in everything, including the number of test cases 2018 | * loop up to the correct variable! (n vs m) 2019 | * Basic Idea: We want to find number of runners such that each still takes the fastest route. So we model the speed as cost and increase the runner number until that no longer is possible and a more costly cost is taken. That is checkable even with binary search, because we know how much the cost *should* be given n runners. 2020 | 2021 | ### Sith 2022 | 2023 | * Increment your circulators!! 2024 | * use the disjoint_sets. 2025 | * Tracking set sizes must be done by you, not the set library! 2026 | * Apparently sometimes points actually have no neighbours in the triangulation. Check if `circ != 0`. 2027 | * Check your input types!! 2028 | * Apparently you can choose which planet you start on, on every day. 2029 | * Basic Idea: sort the planets by their time-to-live. Then insert the longest-living into triangulation, then the second-longest etc. Each time look at the neighbours of the newly inserted and check their distance. If they are connected, merge their clusters. If further away ones are in range, they are also in range of the neighbour between us and them, so they are already connected now - no need to check them manually. 2030 | Every cluster always contains only planets it would want to take. So never add a planet that dies sooner than the best clustersize. Instead, stop then. Nothing is getting better from now. The best cluster will never get better, and others only as good. 2031 | * `uint * uint` can not become a long 2032 | * Check if the cluster is the same we're already in! 2033 | * re-find your union representatives more often 2034 | 2035 | ### Fleetrace 2036 | 2037 | * Maximum Matching: do a mincost maxflow and allow every entry to directly flow to the sink with maximum cost. Then it's just a cost minimzation. (Which we want, because otherwise the maxflow could be two boatpairs instead of one better boatpair) 2038 | * Since all boats participate, the negation of score to cost and back is easier. 2039 | total score = num_boats * MAXCOST - total_cost. 2040 | * Don't add edges per pair to each boat! These are supposed to be unique. 2041 | * Read inputs in the right order! 2042 | 2043 | ### Secret Service 2044 | 2045 | * initialize the vectors backing an iterator property map to its size. 2046 | * Dijkstra apparently sets unconnected components to INT_MAX? 2047 | * think more about weird edge cases that might invalidate the whole approach. But also, think less about edge cases. .... 2048 | * Basic Idea of Solution: 2049 | * compute a bipartite graph and minimize the longest edge. 2050 | Computing the graph requires multiple shelter vertices where the second costs $entering\text{-}time$ more than the first. For each shelter, compute and remember dijkstra so we can set up the big bipartite graph with edges of length shortest path. 2051 | * Then on the bipartite graph we want to minimize the longest edge. We do a maximum cardinality matching for that. and check if the matching indeed contains all agents. Then do binary search on the time. 2052 | * Don't use the `out` files as input! 2053 | 2054 | ### Germs 2055 | 2056 | * For each bacterium, only the closest one (and the borders) matter. So use delaunay to get the closest other and compute the time to live. 2057 | 2058 | * Always make sure to increment circulators, check them for null, and check them for infiniteness. 2059 | 2060 | * Binary search with an unknown upper end? Well, exponential search is one option... but just choosing some lower initial guess without modifying the boundaries is another :D 2061 | Of course important to make sure that `firstrun` is correctly set/reset. 2062 | 2063 | ```c++ 2064 | mid = firstrun ? initialguess : (left + (right - left)/2); 2065 | firstrun = false; 2066 | ``` 2067 | 2068 | ### Idefix 2069 | 2070 | * when using a circulator, always keep in mind that it will be null/0 if there is no neighbour yet inserted. Even if it is a triangulation, because it has dimension 0 then. 2071 | * use `max_element`, not `max`, on a vector. 2072 | * When doing BFS, think about which children should be visited. Especially don't forget that you need to filter the edge length. 2073 | * If something is wrong, maybe you're accessing the wrong iterator? debug prints and small tests help. 2074 | * binary search over the limited space of relevant distances, not over all possible distances, for the last few percent score. For that, simply take a sorted list of relevant radii and do binsearch over the vector indices. 2075 | 2076 | ### Algocoon 2077 | 2078 | * A mincut/maxflow assigns all other vertices to the optimal set, given a source vertex in set 1 and a sink vertex in set 0. 2079 | Hence, to find the best cut if we don't know the source and sink, we have a better option than trying all $n^2$ pairs: Fix the first vertex as in set zero and try every other sink. Maybe our assumption was right and the first vertex actually is in set zero - then we're good. Or we were wrong, so we do the same thing again fixing it to set one instead. 2080 | The reason this works is because given any source in the starting set is supposed to give you a minimum cut that is as good as it gets - it either assigns all other vertices "correctly" (so it's the same cut) or does so in a different way. But the different way only happens if the others are not reachable from the source, which results in a smaller mincut. So a better one. 2081 | * There's no need to worry about enclaves of one set inside the other - we can just engulf it. Except when the enclave contains the sink, but if there is only one enclave, there's no added difficulty. 2082 | 2083 | ### First hit 2084 | 2085 | For the last 10%, one needs to compare segments instead of rays. Apparently, that is faster - because you have less intersections. Some repos also shuffle the input. I'm still to slow with both, despite using different Kernels for differnet checks. 2086 | 2087 | ### Switzerland 2088 | 2089 | > The first piece of information that leads to this solution is that we don't need to know what provinces are part of that free-standing subset, only if it exist or not. When exploring possible way how to represent the problem on of the seemingly possible options is flow. We create source and sink. If province has positive balance it has incoming edge from the source (of such value). If negative it has an outgoing edge to sink. All debts will be modeled as edges between the provinces. In this definition a free-standing subset is a group of provinces that will receive more flow (money) from the source than will need to give. In terms of flow we can say it will create a bottle neck in the network. This is the main idea. If there is such subset than some of the money will stay and we detect that the flow is smaller that the sum of the incoming edges. Therefore we just call the maximum flow algorithm and compare the result with the expected value. 2090 | > [- Simon Hrabec](https://github.com/simon-hrabec/Algolab2020/tree/main/Week%2010%20-%20Asterix%20in%20Switzerland) 2091 | 2092 | I think there are a few things unclear in the task. They remained unanswered on moodle. But I'm no longer certain whether the (b) even matters. Because a bit later at least "free-standing" is well defined. $\Rightarrow$ If a task is unclear, try ignoring the unclear part and see if it is solveable despite that. 2093 | 2094 | > a) In the test cases 2 and 3 it is claimed that for all i and j we have the debt from i to j larger than the sum of absolute values of balances. That seems to be not true though, because for some i,j the debt is actually zero (and hence not provided as input). 2095 | > 2096 | > b) The sentence "One or several provinces want to form a new union, so that the debts among them can be 2097 | > canceled." is ambiguous. I understand it to mean that whichever provinces form a union discard all the union-internal debts ("so that ..." is a goal). My friend understands it to mean that only provinces that have debts to each other in a way that they would cancel out are allowed to form a union ("so that ..." is a constraint). 2098 | > Which one is true? It's hard to tell based on the tests, since they have rather big numbers 2099 | 2100 | The question in the exercise is whether there is a group such that the money edges into it sum up to strictly more than the money edges out of it. 2101 | If modelled as flow, this means that there is some money not drained out. And for every non-freestanding unit there are more draining edge capacities than incoming ones, so it's an "if and only if" relationship. 2102 | Model: Flow from source for positive initial balances, and to sink for negative initial balances. If a negative balance is filled, cool - if it's overfilled, the outflow is limited compared to the inflow. 2103 | 2104 | ### Antenna 2105 | 2106 | Build a `min_circle` from CGAL with exact constructions but not sqrt. Then build a new mincircle with exact and sqrt from the support set which is always small. now we can compute the square root of the radius easily. 2107 | 2108 | ### Tracking 2109 | 2110 | * at least $k$ rivers have to be taken (edges). So I build a graph with $k+1$ layers. Only the rivers allow advancing to the next layer, but they also exist as normal edges in the last layer, because maybe we use them in addition. (e.g. very long line of only rivers). 2111 | * tricky when the graph is bidirectional: $a\rightarrow b$ and $b\rightarrow a$ both need to go to the next layer. 2112 | 2113 | ### Buddy Selection 2114 | 2115 | We need to find out whether there is a solution that is better than given for each pair. So we remove all options that are worse than given or equal, and then we just need any matching. So we run the Maximum Cardinality Matching. 2116 | 2117 | The alternative idea of using a Maximum Weighted Matching does not work. It would maximize the sum, not the minimum. 2118 | 2119 | ### Boats 2120 | 2121 | Pretty straightforward greedy EDF. 2122 | 2123 | 1. Sort boats by position of their ring. 2124 | 2. for each ring, in order, grab a boat and shift it as far left as possible. Because we want to guarantee as much space for the future as possible (if we were to actually take this boat). Now consider all the boats that we still overlap with because their ring is in our space. Of all those conflicting, choose the one that ends first, to give the future space. 2125 | 3. skip boats we did not choose and are now disabled. 2126 | 2127 | Proof: If our solution were not optimal, the optimal solution chooses a boat that is further to the right ending. If that ship is okay, then ours is also okay though. 2128 | 2129 | ### Octopussy 2130 | 2131 | Always defuse the soonest exploding bomb. If that requires defusing others first, defuse those others on the way. 2132 | 2133 | Reasoning: If we don't defuse the soonest bomb first, it will explode sooner or later. There may be an alternative way, but it would at best be just as good I think. The best time to defuse all bombs is the best time to defuse all-1 bombs plus the time of the last bomb, no matter the ordering. 2134 | 2135 | Simplementation: Starting from the root, store for each node the earliest value it or its parent explodes. Then always take the soonest first? I.e. sort and check? 2136 | 2137 | ### Surveillance 2138 | 2139 | I first understood this wrong and only made one graph which connects from the photos to the police stations. But what if police can't reach the photo? That's why we need two layers in the graph. And the photos allow moving from one layer ot the other. 2140 | 2141 | ### Knights 2142 | 2143 | * literal "corner cases" are different from the rest 2144 | 2145 | * It's a grid? Let's do Flow, not DP 2146 | 2147 | * Vertex with an entry and an exit for a vertex capacity. 2148 | 2149 | * Make use of tests constraints for thinking. in this case: 2150 | 2151 | ```c++ 2152 | /* In the case c==1 it is obvious that we need some magic 2153 | to prevent knights from using the intersection after it has been used. 2154 | 2155 | In the case c==2 I made the assumption that an intersection can never 2156 | be entered and left by more than 2 knights anyway, 2157 | so I don't need further checks. 2158 | An intersection with a there-spawned knight on it can be used by a second 2159 | knight without problem. A third could also sneak in, 2160 | because the spawning of the original knight did not fill any pipe yet, 2161 | but then we only have one way out left. 2162 | So at most two can leave any intersection. 2163 | This makes the case of capacity c >= 2 easier. 2164 | 2165 | For c == 1, we build a complex vertex construct consisting of 2166 | an entry-vertex, and an exit-vertex. A one-way connection with 2167 | capacity c goes from entry to exit. 2168 | From all surrounding complexVertexes exit-vertexes a line goes to 2169 | the entry-vertex. But none back. The connection back comes from 2170 | the exit-vertex. 2171 | Of course, each one-directional connection still needs a second 2172 | 0-capacity line back for the flow algo. 2173 | 2174 | For ease of implementation I will not handle c==1 seperately, 2175 | despite the fact that it could be faster. */ 2176 | ``` 2177 | 2178 | ### Legions 2179 | 2180 | * Try use a simpler geometric approach. 2181 | * Working with the normal vector can work, but in this case it's easier to say the distance (speed times time) should be small enough. Then flip the sign if the start point is on the other side of the line. 2182 | 2183 | ### Return of the Jedi 2184 | 2185 | * Precompute the largest distance edge below each MST node 2186 | 2187 | * Then instead of finding cycles and walking them, find the common ancestor of the leafs with binary search. 2188 | 2189 | * Take precomputed longest edge in const time. 2190 | 2191 | * Simpler approach: build MST and then do as [cheahni](https://github.com/chaehni/AlgoLab/blob/master/2019/week10/return_of_the_jedi/src/algorithm.cpp) does and loop over the edges in the original graph - try adding each one and check out the difference if we took that one and removed the largest in the path instead. For `largest()`, he simply does a DFS and stores it. For some reason, my BFS approach is slower... and wrong. Maybe because there's less tracking of memory involved? 2192 | 2193 | > sägemer en MST a->b->c->d->e->f. Denn wenn ich zersch c->f querie und denn a->f denn isch no problem, es reused was ich computed han. Aber wenn ich b->e querie und nacher a->f denn berechnets es vo neuem. 2194 | 2195 | ==> do DFS in such cases. it's easier. 2196 | 2197 | * If you use a filtered graph, make sure to copy it for speedup, but do not declare the target graph's vertex number in advance - else all existing vertices are still there. 2198 | 2199 | * `boost::edge()` seems to be slow. [avoid](https://stackoverflow.com/q/66043842/2550406). 2200 | 2201 | ### Placing Knights 2202 | 2203 | * Maximum Independent set 2204 | * Bipartite graph ==> can look at slides for advanced flows 2205 | * Make Sure you're actually adding reverse edges for the flow. Especially when it segfaults! 2206 | I was adding the edges with `boost::add_edge` despite having created their edge adder. 2207 | 2208 | ### Real Estate Market 2209 | 2210 | * Given some rules and something to maximize. We enforce the rules as flow (so that even a maxflow is still legal) and then choose among the maxflows the best profit using *cost*. 2211 | 2212 | * If e.g. every buyer can only buy one house, we give them only one incoming capacity. 2213 | * If e.g. every state allows only $n$ sales, we give it $n$ drain capacity. 2214 | 2215 | ### Iron islands 2216 | 2217 | Split task in two: either $pyke$ is involved somewhere in the middle, or it is not involved or only at the start. If it is not involved, just do sliding window from pyke in every direction. Doing the sliding window, keep track of plans that don't involve all people and to involve pyke - then later test those combinations. 2218 | 2219 | Putting things into sets is hard. So use pointers instead, keeping the actual things safely in a vector. 2220 | 2221 | ### Fighting Pits Of Meereen 2222 | 2223 | $2^n$ DP storage bitshift trickery? I haven't fully done that yet 2224 | 2225 | Quote from moodle: 2226 | 2227 | > Suppose you want to encode x and y into one integer and you know that $0<=x<=4$ then you can use $z=y*5 + x$ as an encoding of $x$ and $y$ and get back $x$ and $y$ by $x=z%5$ and $y=(z-x)/5$. 2228 | 2229 | [Zilin Wang](https://github.com/ZilinIB/ETH-Algolab/blob/master/week11/fighting%20pits%20of%20meereen/src/main.cpp) has linked his code and it looks simpler than the others I have seen. He has a DP table where `i` represents the $i$-th fighter, $j$ represents the difference between left and right queue and from that he computes the next value for both queues. If the resulting score is better than what is already stored, he saves it. And the variety of each queue is carried along while computing, once for the left and once for the right queue. All the types encoded into a small number. 2230 | 2231 | ### Car Sharing 2232 | 2233 | * Multi-Layered graph for timesteps. Allow parking infinitely for no profit and all missions as per their time from start to end. 2234 | * Be faster with a smaller graph, so no need for vertices that *only* park and have no mission start or end. 2235 | 2236 | ### India 2237 | 2238 | Idea: We want the coffers as flow and find the largest flow fulfilling the budget. So we just do mincost-maxflow for a given num coffers and vary the number until our budget is enough. 2239 | 2240 | ### Lannister / Casterly Rock 2241 | 2242 | * Right angle to a vector is swapping the coordinates and negating one of them (in 2D). Not just negating one of them like I just did. Make sure to quickly check your geometric whims! 2243 | 2244 | * The "direction" of a line is given by the normal vector $\left(\matrix{a\\b}\right)$, which can be read straight out of the line equation $ax + by +c = 0$. The direction the normal vector points towards is "above" the line - i.e. the points on that side fulfill $ax + by + c \geq 0$. 2245 | 2246 | * To disallow a line choosing both directions in LP, limit either $a$ or $b$ to be only positive. If $a > 0$ then the line will never be completely vertical. If $b > 0$ then the it will never be fully horizontal. 2247 | 2248 | * To enforce $a > 0$ it is okay to set its lower bound to $1$. Because the ratio between $a$ and $b$ is what it's important, and $b$ is still free. 2249 | 2250 | * To do multiple checks where some have precedence, we can reuse the `lp` and do multiple solutions from it. Adding the new constraints only after solving for the first time. 2251 | 2252 | * If a maximum of minima is searched, use a variable representing the minimal length and subtract it from all, requiring that the result is still above zero. Also require that the minimal length is above zero. Maximize it, given the constraint that all normal lengths are longer or equal to it. 2253 | That means: 2254 | 2255 | * $\mathtt{distance\_equation} - \mathrm{MINDIST} \geq 0$ 2256 | * $\mathrm{MINDIST} \geq 0$ 2257 | * $\text{maximize }\mathrm{MINDIST}$ 2258 | 2259 | * If we want a certain distance with all points inside, but don't know which side a point is on, we can create two other lines. E.g. given $ax + by +c = 0$ we can require 2260 | 2261 | * $ax + by + c + L \geq 0$ 2262 | * $ax + by + c - L \leq 0$ 2263 | 2264 | The first of these two lines is the one that lies lower - because the point $(x,y)$ is smaller in $y$ for the same $x$. So we want the points to be above it. And below the other one that lies higher. 2265 | 2266 | * If you need to minimize a sum of terms, try formulating it and shifting things around. Maybe you can end up with a form $sum_1 \cdot A + sum_2 \cdot B + sum_3 \cdot C \leq 0$. 2267 | 2268 | * The right angle in 3D would be done with the cross product. Not that you will need it at the exam, but just take any non-perpendicular nonzero vector and compute the cross product - and now you have a perpendicular vector. 2269 | 2270 | ### Exam: Mad-Eye Moody 2271 | 2272 | Task: Choose as many members of the Order as possible under the constraint that they all start at the start vertex and all reach the end vertex at the same time, and also under the constraint that they are as fast as possible. 2273 | 2274 | 1. Figure out how fast is possible. Since every edge can only be used a given number of times, we use that as the capacity in a flow graph. Every edge must also have a backwards connection with the same capacity, because they can be used in both directions. 2275 | You might think it's problematic because we now have to ensure that the capacity of the edge and its reverse edge added up are less or equal the number allowed... but in our flow this is not a problem. Because if one person crosses in one direction and another in the other direction, we could also just let them both continue as normally and they would both be faster. 2276 | Limit the flow to $1$ and run a mincost maxflow algo, where the cost is the time an edge takes. Now we know how fast the fastest path is. 2277 | 2. If the number of people (= the flow from the source) is small enough to fulfill all constraints, the criterion as follows is fulfilled: $\mathtt{fastest\_path\_cost} * \mathtt{num\_people} == \mathtt{total\_flow\_cost}$ 2278 | So run the flow cost computation multiple times with different amounts of in-flow and verify this criterion. The largest $\mathtt{num\_people}$ that fulfill the criterion is the number we are looking for. 2279 | 3. Do binary search to find that. 2280 | 4. Using an upper bound of `LONG_MAX` is a bit much and causes TLE. There are several options: 2281 | * Set the first run `mid` to `20000` to ensure that all small values are found quickly while all large values have still about the same runtime. 2282 | * Use exponential search. 2283 | * Use the largest possible flow out from the source / in to the sink as maximum. 2284 | * Run it once with `LONG_MAX` and then keep the resulting maxflow value as upper bound. 2285 | 2286 | ### Exam: Harry Potter 2287 | 2288 | *caveat emptor: I did not run this.* 2289 | 2290 | Harry has several friends. He himself is $p_2$, Prof. Slughorn is $p_1$, there is no $p_0$ and every $p_k$ is some friend. Larger $k$ means he trusts them less. Which isn't really relevant but we have to find a small $k$ and use only the people up to $p_k$. 2291 | 2292 | They want to poison Slughorn in a way that does not raise too much suspicion. Every transaction between two people adds some amount to the sum of suspicions, which may at most be $c$, otherwise they are `Busted!`. 2293 | 2294 | Every interaction $p_a \rightarrow p_b$ has a suspicion level $s_{ab}$. Also, during every interaction some amount of the potion might dissipate. That is given by a rational number $e_{ab} = \frac{e'_{ab}}{e''_{ab}}$ such that the remaining amount of potion is $e_{ab} \cdot \mathtt{potion\_incoming}$. 2295 | 2296 | Harry alone has potion at the start - a total of $a$. In the end, you must give at least $b$ potion to Slughorn. Find a solution such that $b$ is reached and the total sum of suspicion is below $c$. If there is none, output `Busted!`, otherwise print the smallest $k$ needed for a working solution and the value $s$ representing the maximal interaction suspicion in use, which should be as small as possible given that setting of $k$ people. 2297 | 2298 | #### Samples 2299 | 2300 | ![image-20210130155508086](README.assets/image-20210130155508086.png) 2301 | 2302 | The last sample had solutions `3, 1` which does not make sense. ~~the second number, representing the largest pairwise suspicion should be $0$ in my opinion~~. However, here's how that makes sense: float numbers are allowed: 2303 | 2304 | > Okay ich check de test case jetzt glaub. Wenn mer nur `k = 2` hetti faileds obviously wil zum 1 unit verabreiche machsch suspicion total of `2` und das isch zviel. 2305 | > 2306 | > Aber wenn nur d edges 2->3 und 3->1 benutze würsch brüchtisch meh potion als initially verfüegbar. Wil de harry hät am afang 3. `3 * 0.5 = 1.5, 1.5 * 0.5 = 0.75 < 1`. 2307 | > 2308 | > Also muess mer beidi Pfad benutze. Zersch mal so viel wie mir eus erlaube chönd durch de suspicious path. Sägemer `0.5` units, d.h. total suspicion of `1`. Grad na legal. Denn die restliche foif zweitel potion id edge 2->3 gäh, resultiert in `5/4` units. Denn vo 3->1 gäh, resultiert in `5/6`. In total also verabreicht: `5/6 + 3/6 = 8/6 > 1`. 2309 | 2310 | #### Solving as LP 2311 | 2312 | There are too many individual constraints as a flow. I spent a lot of time thinking about it as a flow or DP, but I think it's an LP problem. 2313 | 2314 | The variables are one per interaction. That should work because we have a small number of people and interactions. Each $edge_{ab} \forall a,b$ represents how often the interaction was taken. 2315 | 2316 | Suspicion per interaction per unit is given, and denoted here as $sus_{ab}$. 2317 | 2318 | * All edges are used at least $0$ times. 2319 | 2320 | * The sum of incoming edges times their efficiency must be $\geq d$ for the Slughorn vertex. 2321 | * The sum of outgoing edges must be $\leq a$ for the Harry vertex. 2322 | * The sum of each Vertex $v$'s outgoing edges must be smaller equals to the sum of each incoming edge times its efficiency. 2323 | * The sum of all $edge_{ab}\cdot sus_{ab}$, representing the total suspicion, must be $\leq c$ 2324 | * We want to minimize the variable $max\_suspicion$. We do that by requiring that every edge suspicion in use is less than or equals to $max\_suspicion$. 2325 | $sus_{ab} - max\_suspicion \leq 0$. 2326 | And minimize $max\_suspicion$. But we only care about the edges in use... 2327 | we could filter out unused edges by multiplying with $edge_{ab}$ ~~but that would cause problems if an edge was used a different number than never or once.~~ 2328 | And that's exactly how it's meant. They said we need to find the smallest integer $s$ that the suspicion raised by each pair is at most $s$. And since the suspicion caused scales with the amount of potion transported... 2329 | $edge_{ab}\cdot sus_{ab} - max\_suspicion \leq 0$. 2330 | 2331 | To choose the smallest $k$ that works, we could do a linear search or binary search using that LP multiple times. 2332 | 2333 | For the first two test cases, we are told that the number will always be $k = n$ though. For the first test case, we also can assume that the largest edge suspiciousness $sus_{ab}$ in use is a known constant. So the task is only to find out whether a solution exists. For this, it's okay to not know how to model the $max\_suspicion$ optimization. 2334 | 2335 | For the second test case, we add our $max\_suspicion$ constraint. 2336 | 2337 | For the third and fourth test case, we perform binary or linear search on which friends we add. Not sure if it would be possible to solve that inside the LP. 2338 | 2339 | ## Exam Environment in HG 2340 | 2341 | ### Vim 2342 | 2343 | Use `gvim`, otherwise copypaste does not work. 2344 | 2345 | Highlight works after writing vimrc. 2346 | 2347 | ```vimrc 2348 | set mouse=a 2349 | set tabstop=4 2350 | set shiftwidth=4 2351 | set softtabstop=4 2352 | set expandtab 2353 | filetype plugin indent on 2354 | colo torte 2355 | ``` 2356 | 2357 | ### VSCode 2358 | 2359 | download from the docs tools folder `vscode.cpptools-1.1.1-linux.vsix` and `algolabVS.sh`. grep the latter with `cat algolabVS.sh | "code --"` and execute the given command in the downloads folder. 2360 | 2361 | Then make `algolabVS.sh` executable and run `./algolabVS.sh install`. Reopen terminal or `source ~/.bashrc` and then move to project folder. 2362 | 2363 | Per project, initialize with `algolabVS init`. Open VSCode with `code .`. 2364 | 2365 | the `algolabVS init` already creates cmake and everything. 2366 | 2367 | Debugging a different file: just paste the contents over, it's the easiest way. 2368 | 2369 | place input data in `debug_test_data.txt` 2370 | 2371 | To test things, do CtrlshiftP and then do the "run test task" "test" 2372 | 2373 | ### Keyboard 2374 | 2375 | Windows key -> language and regions -> German Switzerland No Dead Keys. 2376 | 2377 | ### dot 2378 | 2379 | Just works! If you managed to write a graph dot output, you can run 2380 | 2381 | ```bash 2382 | dot -Tsvg -omygraph.svg mygraph.dot && firefox mygraph.svg 2383 | ``` 2384 | 2385 | ## Licence 2386 | 2387 | [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/legalcode) 2388 | 2389 | And you are welcome to pull request for adding some missing infos and correcting mistakes I made! 2390 | --------------------------------------------------------------------------------