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));
  }
}

No comments:

Post a Comment