From 5f60a31bc7524be77a92c009698e204958904208 Mon Sep 17 00:00:00 2001
From: fer22f <fer22f@gmail.com>
Date: Tue, 31 Mar 2020 00:47:29 -0300
Subject: [PATCH] Add Algorithms Live notes

---
 .../Algorithms-Live-14-Exchange-Arguments.md" | 140 ++++++++++
 .../Algorithms-Live-15-Split-the-Sequence.md" | 248 ++++++++++++++++++
 2 files changed, 388 insertions(+)
 create mode 100644 "anota\303\247\303\265es/algorithms-live/Algorithms-Live-14-Exchange-Arguments.md"
 create mode 100644 "anota\303\247\303\265es/algorithms-live/Algorithms-Live-15-Split-the-Sequence.md"

diff --git "a/anota\303\247\303\265es/algorithms-live/Algorithms-Live-14-Exchange-Arguments.md" "b/anota\303\247\303\265es/algorithms-live/Algorithms-Live-14-Exchange-Arguments.md"
new file mode 100644
index 0000000..a967db2
--- /dev/null
+++ "b/anota\303\247\303\265es/algorithms-live/Algorithms-Live-14-Exchange-Arguments.md"
@@ -0,0 +1,140 @@
+# Exchange Arguments
+
+# Working out a solution comparing it to every other solution
+Given n. Pick a and b such that a + b = n and maximize ab.
+
+Case n is even. Let's prove that a = b = x = n/2 is the best solution.
+  We then have ab = x^2.
+  Let's assume that a is the biggest number such that:
+  a = x + delta
+  b = x - delta
+  All other solutions are where delta >= 1.
+  `ab = (x + delta)(x - delta) = x^2 + delta*x - delta*x - delta^2
+  = x^2 - delta^2 < x^2`
+  (because delta is a position number)
+
+# The exchange problem
+Generally, greedy doesn't work for coin exchanges, for example:
+
+Get to 1000 with coins (502) (499) (1)
+We get (502) + (1)*498 with greedy, instead of (499)*2 + (1)*2.
+
+# Probing
+Let's try these coins: (25) (10) (5) (1).
+
+## What properties the optimal solution has
+
+Suppose we have 3*(10), we can replace it with (25) + (5).
+So the max number of nickels is 2. The optimal solution, then, never has
+more than 2 nickels.
+
+Suppose we have 2*(5), we can replace it with (10).
+So the max number of dimes is 1. The optimal solution, then, never has more
+than 1 dime.
+
+Suppose 5*(1), we can replace it with (5). So at most 4 pennies.
+
+Suppose 2*(10) + 1*(5), but we can replace it with (25). So we can't have
+both.
+
+Anything that less than (25), amounts to 24 or less. Let's do it by cases:
+  * Max number of each: (10)*2 + (1)*4 = 24.
+  * Max number with 10: (10)*1 + (5)*1 + (1)*4 = 19.
+
+## Proof
+O*: Optimal solution
+G: Greedy solution
+
+Let q  = number of (25) in O*.
+    q' = number of (25) in G.
+Greedy guarantees that q > q', it will be the same or worse, so q `<=` q'.
+Suppose q `<` q'. However, we have at most 24 using the other coins, so we
+can't replace them with a (25), thus q = q'.
+
+Let d  = number of (10) in O*.
+    d' = number of (10) in G.
+Suppose d `<` d'. We can't use the other coins to make 10, we can have at
+most 1*(5) + 4*(1). Thus, d = d'.
+
+Same argument with nickels (5), and pennies (1).
+
+Thus, O* = G.
+
+# Minimizing dot product
+```
+v_1 = <-3, 4, 2>
+v_2 = <1, -4, 11>
+```
+Rearrange the numbers such that the dot product is minimized.
+
+dot = \sum^{k}_{i=0} v_1[i] v_2[i]
+    = -3 * 1 - 4 * 4 + 2 * 11
+    = -3 - 16 + 22
+    = 3.
+
+Sort the first one, because it doesn't matter.
+```
+v_1 = <-3, 2, 4>
+```
+Then think of an arrangement for `v_2`.
+
+What if we match the biggest things with the smaller things?
+```
+v_1 = <-3, 2, 4>
+v_2 = <11, 1, -4>
+
+-33 + 2 - 16 = -47
+```
+Does this always work? (Yes).
+
+```
+x_i <= x_j for every i < j
+y_i < y_j <=> swap y_i, y_j
+```
+
+Let's say....
+```
+  x_i*y_i + x_j*y_j - (x_i*y_j + x_j*y_i)
+= x_i*y_i + x_j*y_j - x_i*y_j - x_j*y_i
+= x_i(y_i - y_j) + x_j(y_j - y_i)
+= x_i(y_i - y_j) + x_j(-1)(y_i - y_j)
+= (x_i - x_j)(y_i - y_j)
+```
+
+Where `x_i - x_j <= 0` and `y_i - y_j < 0`, which means
+`(x_i - x_j)(y_i - y_j) >= 0`.
+So we can conclude that swapping decreases or maintains the same cost (for
+something that doesn't meet the property of being ordered decreasingly).
+
+# Solving "Problems"
+A problem is defined by the tuple (reward, decay (in seconds), time needed to solve).
+Maximize reward, which problems do you solve and at what order?
+
+## Simplification
+What if we knew which problems to solve? We need to only find the solve order.
+
+Tip: Explore the cost exchange to find the property to sort when 2 problems swap.
+
+So we have P_1 and P_2.
+
+reward_1 = m_1 - d_1*t_1 + m_2 - d_2(t_1 + t_2)
+reward_2 = m_2 - d_2*t_2 + m_1 - d_1(t_1 + t_2)
+
+When does `reward_1 >= reward_2`? It would be when `reward_1 - reward_2 >= 0`.
+
+So let's do algebra:
+
+```
+  m_1 - d_1*t_1 + m_2 - d_2*t_1 + d_2*t_2
+- m_2 + d_2*t_2 - m_1 + d_1*t_1 + d_1*t_2
+= d_1*t_2 - d_2*t_1 >= 0
+  d_1*t_2 >= d_2*t_1
+```
+
+So my sorting criteria is `d_2*t_1 <= d_1*t_2`, this is the optimal ordering.
+
+## Steps to solve
+1) Sort by optimal criteria
+2) Run dynamic programming solution to know which problems to take
+  State: which problem, time used
+  Transitions: solve problem, skip problem
diff --git "a/anota\303\247\303\265es/algorithms-live/Algorithms-Live-15-Split-the-Sequence.md" "b/anota\303\247\303\265es/algorithms-live/Algorithms-Live-15-Split-the-Sequence.md"
new file mode 100644
index 0000000..1052557
--- /dev/null
+++ "b/anota\303\247\303\265es/algorithms-live/Algorithms-Live-15-Split-the-Sequence.md"
@@ -0,0 +1,248 @@
+# Split the Sequence
+Given k, partition an array to maximize the sum of the multiplication of the sum of the values at each step.
+For example, for k = 3, the optimal is:
+
+4   1 3   4 0   2 3
+4 | 1 3   4 0   2 3    4*13 =  52
+4 | 1 3 | 4 0   2 3    4*9  =  36
+4 | 1 3 | 4 0 | 2 3    4*5  =  20
+Result                      = 108
+
+## Approach 1
+The bounds are `1 <= k < n <= 10`. So we could have a solution in `n!`.
+
+## Approach 2
+The bounds are `1 <= k < n <= 50`. Let's use MCM (Matrix Chain Multiplication)
+style DP. Or parenthesis placement style DP.
+
+(   4 1     3 2   ) ( 4 5 6 1 )
+( ( 4 1 ) ( 3 2 ) ) ( 4 5 6 1 )
+
+Let's place the outer parenthesis first, then gradually bind towards the
+inner parenthesis. So:
+
+* State: Left side, right side (what the range of the values being parenthesized), k (how many splits left)
+* Transitions:
+  * Brute force split point (all n possible split points)
+  * Brute force k distribution also, (k_1) and (k_2) where k_2 = k - k_1.
+
+State size: O(n^2 k).
+Transitions: O(n k).
+DP is O(n^3 k^2) which works up to 50.
+
+```java
+long go(int i, int j, int k) {
+  if (i == j) return 0L;
+
+  if (memo[i][j][k] != null)
+    return memo[i][j][k];
+
+  long res = 0;
+  long sumTotal = 0;
+  long sumLeft = 0;
+  for (int m = i; m <= j; m++)
+    sumTotal += vs[m];
+
+  for (int m = i; m < j; m++) {
+    sumLeft += vs[m];
+    long sumRight = sumTotal - sumLeft;
+    // every split point
+    for (int k1 = 0; k1 < k; k1++) {
+      int k2 = k - k1-1;
+      long splitCost = sumLeft * sumRight;
+
+      long r1 = go(i, m, k1);
+      long r2 = go(m+1, j, k2);
+
+      long rr = r1 + r2 + splitCost;
+      res = Math.max(res, rr);
+    }
+  }
+
+  return memo[i][j][k] = res;
+}
+
+public B(in, out) {
+  memo = new Long[N][N][K+1];
+  long res = go(0, N-1, K);
+}
+```
+
+## Approach 3
+Greedy Observation: The order of the splits doesn't matter.
+
+Let's prove it:
+
+A | B | C
+  1   2
+  2   1
+
+Two splits, three groups of numbers A, B, C. We want to prove that
+
+```
+(AB)C = A(BC)
+```
+
+The cost of doing the first split first is `A(B + C) + BC = AB + AC + BC`.
+The cost of doing the second split first is `(A + B)C + AB = AB + AC + BC`,
+which are equal.
+
+So let's make the splits in order!
+
+So we have a new DP:
+* State: Start of the sequence we are at, which k is left
+* Transitions: All ending points
+
+So we get O(n^2 k).
+
+We are expected to pass these bounds: `1 <= n <= 1000`, `1 <= k <= min(n-1, 200)`.
+
+```java
+long go(int i, int k) {
+  if (k == -1 || i == N)
+    return 0L;
+
+  if (memo[i][k] != null)
+    return memo[i][k];
+
+  long res = 0;
+  for (int j = i; j < N; j++) {
+    long splitCost = sum[i] * (sum[j+1] - sum[i]);
+    long rr = splitCost + go(j+1, k-1);
+    res = Math.max(res, rr);
+  }
+
+  return memo[i][k] = res;
+}
+
+public B(in, out) {
+  memo = new Long[N][K+1];
+  sum = new int[N+1];
+  for (int i = 0; i < N; i++)
+    sum[i+1] = sum[i] + vs[i];
+  long res = go(0, K);
+}
+```
+
+## Approach 4
+Bounds of `n <= 10^5, k <= min(n-1, 200)`.
+
+Let's make the splits in reverse order:
+```
+------ splits = b
+  |   | x -> |
+  j   i
+------ sums = a
+```
+The cost of making splits up to and including `i` is `b`.
+The sum of the range up to and including `i` is `a`.
+And there is some sum of costs that is being propagated to the right, `x`.
+
+So when we make a split at position i from right to left, we sum `ax` to the cost,
+and the result is summed to whatever splits we make to the left, `b`, so we
+get `ax + b`, which is a line. So this is Convex Hull Optimization.
+We handle k by computing k and then k + 1.
+
+```java
+public B(in, out) {
+  memo = new Long[N][K+1];
+  sum = new int[N+1];
+  for (int i = 0; i < N; i++)
+    sum[i+1] = sum[i] + vs[i];
+
+  // Convex Hull Optimization
+  long[] prev = new long[N+1];
+  long[] cost = new long[N+1];
+
+  Line[] dq = new Line[N+1];
+  int[][] prevSplit = new int[K][N+1];
+
+  // brute-force all posible k splits
+  for (int k = 0; k < K; k++) {
+    Arrays.fill(cost, 0);
+    int fptr = 0, bptr = 0;
+
+    for (int i = 0; i <= N; i++) {
+      while (fptr+1 < bptr && dq[fptr].getCost(sum[i]) <= dq[fptr+1].getCost(sum[i]))
+        fptr++;
+
+      if (fptr < bptr) {
+        cost[i] = dq[fptr].getCost(sum[i]);
+        // Save optimal split
+        prevSplit[k][i] = dq[fptr].i;
+      }
+
+      Line newLine = new Line(i, sum[i], prev[i]);
+
+      while (fptr + 1 < bptr) {
+        long t1 = dq[bptr-2].to(dq[bptr-1]);
+        long t2 = dq[bptr-1].to(newLine);
+
+        if (t1 < t2)
+          break;
+        else
+          bptr--;
+      }
+
+      dq[bptr++] = newLine;
+    }
+
+    for (int i = 0; i <- N; i++)
+      prev[i] = cost[i];
+  }
+
+  out.println(prev[N]);
+
+  int i = N;
+  int k = K-1;
+  ArrayDeque<Integer> stk = new ArrayDeque<>();
+  while (k >= 0) {
+    i = preSplit[k][i];
+    stpk.push(i);
+    k--;
+  }
+
+  StringBUilder sb = new StringBuilder();
+  while (stk.size() > 0) {
+    sb.append(stk.pop();
+    sb.appenbd(' ');
+  }
+  out.println(sb.toString().trim());
+}
+
+class Line {
+  long a, b;
+  long i;
+
+  Line (int ii, long aa, long bb) {
+    i = ii; a = aa; b = bb;
+    }
+
+  // x is going to be prefix sum, so we just subtract a
+  long getCost(long x) {
+    return a * (x - a) + b;
+  }
+
+  // when a line gets overtaken by another line
+  long to(Line rhs) {
+    // a line start at the position a
+    long lo = Math.max(a, rhs.a);
+    long hi = Integer.MAX_VALUE;
+
+    while (lo < hi) {
+      long m = (lo+hi) / 2;
+      long v1= getCost(m);
+      long v2 = rhs.getCost(m);
+
+      if (v1 <= v2)
+        hi = m;
+      else
+        lo = m+1;
+    }
+
+    return hi;
+  }
+}
+```
+
+Binary search there is not optimal, can be optimized.
-- 
GitLab