back to home

problem link

max independent set

oh god *shudders*

this problem was a debugging nightmare

so the first thing i did was go to the wikipedia page for wth an independent set actually was, and it did not disappoint
scrolling down a little we can see that this problem is equivalent to the clique problem on the complement graph

so from here on out i'll just be talking about the maximum clique problem instead, it makes things a tad bit easier to explain

anyways, we have \( n \leq 40 \)!
this probably means that we have to split crap in 2 then merge somehow...
if you want some other problems that are like this you can go here btw
(or it means \( \mathcal{O}(n^5) \) or something but the chance of that is lesser than my dad coming home with milk)

so whatever, let's just go through all subsets in half of the total graph and see what happens

so here's our halfway dp- ``` dp[ss] = max clique if we can only use the nodes specified in ss (subset) ``` note that another option would be ``` dp[ss] = do all the nodes in the ss form a clique? ``` but it can be seen later on that this definition sucks balls when we try combining the 2 subsets

once we have the definition, we can fill it up with brute force- i'll leave it to the code to explain how that works


so now we have 2 of these `dp` things
so how can we combine them?
for any subset, we need to know all the nodes in the other half that could be "compatible", which in this case means that they're connected to literally every node in our current subset

this might seem like a daunting task, but it actually isn't that bad if you know how bitwise ops work
like the brute force, how this works is in the code

so we have a subset and another subset of all the nodes in the other half that it's compatible with
we can then get a candidate for the max clique with `dp1[ss1] + dp2[ss2]` and then go!

```cpp #include <iostream> #include <vector> using std::cout; using std::endl; using std::vector; inline bool bit_set(const long long& n, int pos) { return n & (1LL << pos); } long long more_bits(const long long& a, const long long& b) { return __builtin_popcountll(a) > __builtin_popcountll(b) ? a : b; } vector<long long> largest_clique(const vector<long long>& adj) { int sz = adj.size(); // just a shorthand vector<bool> valid(1 << sz); // actually the scrapped dp definition lol valid[0] = true; for (int n1 = 0; n1 < sz; n1++) { valid[1 << n1] = true; for (int n2 = n1 + 1; n2 < sz; n2++) { valid[(1 << n1) + (1 << n2)] = adj[n1] & (1 << n2); } } for (int ss = 1; ss < (1 << sz); ss++) { if (__builtin_popcountll(ss) <= 2) { continue; } valid[ss] = true; for (int prev = 0; prev < sz; prev++) { if (bit_set(ss, prev) && !valid[ss & ~(1 << prev)]) { valid[ss] = false; break; } } } // here's our actual dp, the one we're gonna combine vector<long long> largest(1 << sz); for (int ss = 0; ss < (1 << sz); ss++) { if (valid[ss]) { largest[ss] = ss; continue; } for (int prev = 0; prev < sz; prev++) { if (bit_set(ss, prev)) { largest[ss] = more_bits(largest[ss], largest[ss & ~(1 << prev)]); } } } return largest; } int main() { int node_num; int edge_num; std::cin >> node_num >> edge_num; vector<long long> adj(node_num, (1LL << node_num) - 1); for (int e = 0; e < edge_num; e++) { int n1, n2; std::cin >> n1 >> n2; adj[n1] &= ~(1LL << n2); adj[n2] &= ~(1LL << n1); } int half1 = node_num / 2; vector<long long> half1_adj(half1); for (int n = 0; n < half1; n++) { half1_adj[n] = adj[n] & ((1 << half1) - 1); } vector<long long> largest1 = largest_clique(half1_adj); int half2 = node_num - half1; vector<long long> half2_adj(half2); for (int n = 0; n < half2; n++) { half2_adj[n] = adj[half1 + n] >> half1; } vector<long long> largest2 = largest_clique(half2_adj); long long best_ss = 0; // here's the part where we combine the two dp things for (int ss1 = 0; ss1 < (1 << half1); ss1++) { // initially, everything in half2 is up for consideration long long ss2 = (1 << half2) - 1; for (int n = 0; n < half1; n++) { if (bit_set(ss1, n)) { // remove any nodes that aren't connected to the current node ss2 &= adj[n] >> half1; } } best_ss = more_bits(best_ss, largest1[ss1] + (largest2[ss2] << half1)); } cout << __builtin_popcountll(best_ss) << endl; for (int i = 0; i < node_num; i++) { if (bit_set(best_ss, i)) { cout << i << ' '; // trailing whitespace doesn't matter } } } ```