Monday, June 15, 2015

Working with an experimental git branch

After several month's experience, I find it very helpful to control source code with git. I think it forces me to write small, standalone pieces of code, which is very good for reading and keeping trace of progress. However, when I am trying to write something new, I don't know what actually should be added, and the feature may not be able to work until I add several bunches of code. So these codes are experimental and may be too ugly to appear in places other than my local machine. A good way to deal with this situation is to create a separate experimental branch, adding whatever code I like. After experimenting and changing, the code becomes stable, and I can split the code in experimental branch into pieces, and rewrite them in another branch, and submit them. After that, I can remove the experimental branch as all its features have been submitted.
In one sentence, I think an experimental branch may be a good choice if the code is bigger than one commit or you are not very sure of what you want.

Sunday, June 14, 2015

How many ways to make a n-node connected graph with k roads?

The problem is as the title. And each node is distinct. Connected graph means each node in the graph can reach any other node in the graph. And there is one road allowed between two nodes. Each of the k roads should be used in the graph. For example, there are 3 ways to connect 3-node graph with 2 roads: A-B-C, A-C-B, B-A-C. There are 1 way to connect 3-node graph with 3 roads. There are 0 ways to connect 3-node graph with 4 or more roads.
The problem seems easy at the first glance. But it is complex to remove the duplications. I think there are two ways to consider the solution. One ways is to add up all ways can be used to connect n-node graph with k roads, the other way is to use C(n*(n-1)/2, k) to subtract all ways that cannot connect n-node graph with k roads. I find the solution in the latter case. So how many ways are there to not connect a n-node graph with k roads? Well, the graph can be made up of two sub-connected graphs, or three sub-connected graphs, or even more. I am sure no one wants to enumerate that after trying it myself. No matter how many sub-connected graphs there are? there must have a sub-connected graph that containing the n'th node. That graph may contains 1 to n-1 nodes, using 0 to k roads, and should be connected. It is in fact a sub problem of the original one. And beside this special sub-connected graph, we don't care about how other connected sub-graphs are connected. Whatever they are, they forms a way to build a non-connected n-node graph with k roads.
So here comes the solution, I can solve it by dynamic programming. let dp[n][k] means how many ways to build a n-node connected graph with k roads.
dp[n][k] = C(n*(n-1)/2, k) - {any n1, k1 to build a sub connected graph containing node n, 1 <= n1 < n, 0 <= k1 <= k, dp[n1][k1] * C((n-n1)*(n-n1-1)/2, k-k1)}.

Below is the code in java:

import java.util.*;
import java.math.BigInteger;

public class Temp {
  BigInteger bigIntAdd(BigInteger a, BigInteger b) {
    if (a == null) {
      return b;
    }
    if (b == null) {
      return a;
    }
    return a.add(b);
  }

  BigInteger[][] buildSelection(int n) {
    BigInteger[][] C = new BigInteger[n+1][n+1];
    C[0][0] = new BigInteger("1");
    for (int i = 1; i <= n; ++i) {
      C[i][0] = C[i][i] = new BigInteger("1");
      for (int j = 1; j < i; ++j) {
        C[i][j] = bigIntAdd(C[i-1][j], C[i-1][j-1]);
      }
    }
    return C;
  }

  BigInteger bigIntMul(BigInteger a, BigInteger b) {
    if (a == null || b == null) {
      return new BigInteger("0");
    }
    return a.multiply(b);
  }

  BigInteger bigIntSub(BigInteger a, BigInteger b) {
    return a.subtract(b);
  }

  public static String answer(int N, int K) {
    Temp instance = new Temp();
    int maxN = Math.max(N * (N - 1) / 2, N);
    BigInteger[][] C = instance.buildSelection(maxN);

    // dp[i][j] means the number of ways to connect i nodes with j roads.
    BigInteger[][] dp = new BigInteger[N + 1][K + 1];
    dp[1][0] = new BigInteger("1");
    for (int i = 2; i <= N; ++i) {
      int minJ = i - 1;
      int maxJ = i * (i - 1) / 2;
      for (int j = minJ; j <= maxJ && j <= K; ++j) {
        // dp[i][j], change i to bitmask 2^(i)-1,
        // dp[2^i-1][j] = C[i*(i-1)/2][j] - dp[mask1][k1] * C[(i-n1)(i-n1-1)/2][j-k1].
        // mask1 means the connected part containing node i-1.
        BigInteger value = C[i*(i-1)/2][j];
        for (int n1 = 1; n1 < i; ++n1) {
          BigInteger countOfMask1 = C[i-1][n1-1];
          int minK1 = n1 - 1;
          int maxK1 = Math.min(n1 * (n1-1) / 2, j);
          BigInteger sum = new BigInteger("0");
          for (int k1 = minK1; k1 <= maxK1; ++k1) {
            BigInteger mulRes = instance.bigIntMul(dp[n1][k1], C[(i-n1)*(i-n1-1)/2][j-k1]);
            sum = instance.bigIntAdd(sum, mulRes);
          }
          sum = instance.bigIntMul(sum, countOfMask1);
          value = instance.bigIntSub(value, sum);
        }
        dp[i][j] = value;
      }
    }
    return dp[N][K].toString();
  }

  public static void main(String[] args) {
    System.out.printf("answer(%d, %d) -> %s\n", 2, 1, answer(2, 1));
    System.out.printf("answer(%d, %d) -> %s\n", 4, 3, answer(4, 3));
    System.out.printf("answer(%d, %d) -> %s\n", 3, 2, answer(3, 2));
  }
}