Algorithm complexity for minimum number of clique in a graph - algorithm

I have written an algorithm which solves the minimum number of clique in a graph. I have tested my backtracking algorithm, but I couldn't calculate the worst case time complexity, I have tried a lot of times.
I know that this problem is an NP hard problem, but I think is it possible to give a worst time complexity based on the code. What is the worst time complexity for this code? Any idea? How you formalize the recursive equation?
I have tried to write understandable code. If you have any question, write a comment.
I will be very glad for tips, references, answers.
Thanks for the tips guys:).
EDIT
As M C commented basically I have tried to solve this problem Clique cover problem
Pseudocode:
function countCliques(graph, vertice, cliques, numberOfClique, minimumSolution)
for i = 1 .. number of cliques + 1 new loop
if i > minimumSolution then
return;
end if
if (fitToClique(cliques(i), vertice, graph) then
addVerticeToClique(cliques(i), vertice);
if (vertice == 0) then //last vertice
minimumSolution = numberOfClique
printResult(result);
else
if (i == number of cliques + 1) then // if we are using a new clique the +1 always a new clique
countCliques(graph, vertice - 1, cliques, number of cliques + 1, minimum)
else
countCliques(graph, vertice - 1, cliques, number of cliques, minimum)
end if
end if
deleteVerticeFromClique(cliques(i), vertice);
end if
end loop
end function
bool fitToClique(clique, vertice, graph)
for ( i = 1 .. cliqueSize) loop
verticeFromClique = clique(i)
if (not connected(verticeFromClique, vertice)) then
return false
end if
end loop
return true
end function
Code
int countCliques(int** graph, int currentVertice, int** result, int numberOfSubset, int& minimum) {
// if solution
if (currentVertice == -1) {
// if a better solution
if (minimum > numberOfSubset) {
minimum = numberOfSubset;
printf("New minimum result:\n");
print(result, numberOfSubset);
}
c++;
} else {
// if not a solution, try to insert to a clique, if not fit then create a new clique (+1 in the loop)
for (int i = 0; i < numberOfSubset + 1; i++) {
if (i > minimum) {
break;
}
//if fit
if (fitToSubset(result[i], currentVertice, graph)) {
// insert
result[i][0]++;
result[i][result[i][0]] = currentVertice;
// try to insert the next vertice
countCliques(graph, currentVertice - 1, result, (i == numberOfSubset) ? (i + 1) : numberOfSubset, minimum);
// delete vertice from the clique
result[i][0]--;
}
}
}
return c;
}
bool fitToSubset(int *subSet, int currentVertice, int **graph) {
int subsetLength = subSet[0];
for (int i = 1; i < subsetLength + 1; i++) {
if (graph[subSet[i]][currentVertice] != 1) {
return false;
}
}
return true;
}
void print(int **result, int n) {
for (int i = 0; i < n; i++) {
int m = result[i][0];
printf("[");
for (int j = 1; j < m; j++) {
printf("%d, ",result[i][j] + 1);
}
printf("%d]\n", result[i][m] + 1);
}
}
int** readFile(const char* file, int& v, int& e) {
int from, to;
int **graph;
FILE *graphFile;
fopen_s(&graphFile, file, "r");
fscanf_s(graphFile,"%d %d", &v, &e);
graph = (int**)malloc(v * sizeof(int));
for (int i = 0; i < v; i ++) {
graph[i] = (int*)calloc(v, sizeof(int));
}
while(fscanf_s(graphFile,"%d %d", &from, &to) == 2) {
graph[from - 1][to - 1] = 1;
graph[to - 1][from - 1] = 1;
}
fclose(graphFile);
return graph;
}

The time complexity of your algorithm is very closely linked to listing compositions of an integer, of which there are O(2^N).
The compositions alone is not enough though, as there is also a combinatorial aspect, although there are rules as well. Specifically, a clique must contain the highest numbered unused vertex.
An example is the composition 2-2-1 (N = 5). The first clique must contain 4, reducing the number of unused vertices to 4. There is then a choice between 1 of 4 elements, unused vertices is now 3. 1 element of the second clique is known, so 2 unused vertices. Thus must be a choice between 1 of 2 elements decides the final vertex in the second clique. This only leaves a single vertex for the last clique. For this composition there are 8 possible ways it could be made, given by (1*C(4,1)*1*C(2,1)*1). The 8 possible ways are as followed:
(5,4),(3,2),(1)
(5,4),(3,1),(2)
(5,3),(4,2),(1)
(5,3),(4,1),(2)
(5,2),(4,3),(1)
(5,2),(4,1),(3)
(5,1),(4,3),(2)
(5,1),(4,2),(3)
The above example shows the format required for the worst case, which is when the composition contains the as many 2s as possible. I'm thinking this is still O(N!) even though it's actually (N-1)(N-3)(N-5)...(1) or (N-1)(N-3)(N-5)...(2). However, it is impossible as it would as shown require a complete graph, which would be caught right away, and limit the graph to a single clique, of which there is only one solution.
Given the variations of the compositions, the number of possible compositions is probably a fair starting point for the upper bound as O(2^N). That there are O(3^(N/3)) maximal cliques is another bit of useful information, as the algorithm could theoretically find all of them. Although that isn't good enough either as some maximal cliques are found multiple times while others not at all.
A tighter upper bound is difficult for two main reasons. First, the algorithm progressively limits the max number of cliques, which I suppose you could call the size of the composition, which puts an upper limit on the computation time spent per clique. Second, missing edges cause a large number of possible variations to be ignored, which almost ensures that the vast majority of the O(N!) variations are ignored. Combined with the above paragraph, makes putting the upper bound difficult. If this isn't enough for an answer, you might want to take the question to math area of stack exchange as a better answer will require a fair bit of mathematical analysis.

Related

Placing blocks and calculating height of the highest tower

Series of k blocks is given (k1, k2, ..., ki). Each block starts on position ai and ends on position bi and its height is 1. Blocks are placed consecutively. If the block overlaps another one, it is being attached on its top. My task is to calculate the highest tower of blocks.
I have created an algorithm which time complexity is about O(n^2), but I know there is a faster solution with the usage of skiplist.
#include <iostream>
struct Brick
{
int begin;
int end;
int height = 1;
};
bool DoOverlap(Brick a, Brick b)
{
return (a.end > b.begin && a.begin < b.end)
}
int theHighest(Brick bricks[], int n)
{
int height = 1;
for (size_t i = 1; i < n; i++)
{
for (size_t j = 0; j < i; j++)
{
if (bricks[i].height <= bricks[j].height && DoOverlap(bricks[i], bricks[j]))
{
bricks[i].height = bricks[j].height + 1;
if (bricks[i].height > height)
height = bricks[i].height;
}
}
}
return height;
}
That's an example drawing of created construction.
You can simply use 2 pointers after sorting the blocks based on their starting positions, if their starting positions match sort them based on their ending positions. Then simply use the 2 pointers to find the maximum height.
Time complexity : O(NlogN)
You can find the demo link here
#include <bits/stdc++.h>
using namespace std;
#define ii pair<int,int>
bool modified_sort(const pair<int,int> &a,
const pair<int,int> &b)
{
if (a.first == b.first) {
return (a.second <b.second);
}
return (a.first <b.first);
}
int main() {
// your code goes here
vector<ii> blocks;
int n; // no of blocks
int a,b;
cin>>n;
for (int i=0;i<n;i++) {
cin>>a>>b;
blocks.push_back(ii(a,b));
}
sort(blocks.begin(), blocks.end(), modified_sort);
int start=0,end=0;
int max_height=0;
while(end<n) {
while(start<end && blocks[start].second <= blocks[end].first)
{
start++;
}
max_height = max(max_height,(end-start+1));
end++;
}
cout<<max_height<<endl;
return 0;
}
Here is a straightforward solution (without skip lists):
Create an array heights
Iterate through the blocks.
For every block
Check the existing entries in the heights array for the positions the current block occupies by iterating over them. Determine their maximum.
Increase the values in the heights array for the current block to the maximum+1 determined in the previous step.
keep score of the maximum tower you have built during the scan.
This problem is isomorphic to a graph traversal. Each interval (block) is a node of the graph. Two blocks are connected by an edge iff their intervals overlap (a stacking possibility). The example you give has graph edges
1 2
1 3
2 3
2 5
and node 4 has no edges
Your highest stack is isomorphic to the longest cycle-free path in the graph. This problem has well-known solutions.
BTW, I don't think your n^2 algorithm works for all orderings of blocks. Try a set of six blocks with one overlap each, such as the intervals [n, n+3] for n in {2, 4, 6, 8, 10, 12}. Feed all permutations of these blocks to your algorithm, and see whether it comes up with a height of 6 for each.
Complexity
I think the highest complexity is likely to be sorting the intervals to accelerate marking the edges. The sort will be O(n log n). Adding edges is O(n d) where d is the mean degree of the graph (and n*d is the number of edges).
I don't have the graph traversal algorithm solidly in mind, but I expect that it's O(d log n).
It looks like you can store your already processed blocks in a skip list. The blocks should be ordered by starting position. Then to find overlapping blocks at each step you should search in this skip list which is O(log n) on average. You find first overlapping block then iterate to next and so on until you meet first non-overlapping block.
So on average you can get O(n * (log(n) + m)) where m - is the mean number of overlapping blocks. In the worst case you still get O(n^2).

How to calculate time complexity of recursions with memoization?

What is time complexity of the code below? I know it has recursive call multiple times so it should probably be 3^n, but still each time it initializes array of length n, which is latter used and it kinda confuses me. What should be time complexity if we would add additional array to apply memoization? This below is solution for Hackerrank Java 1D Array (Hard) task.
public static boolean solve(int n, int m, int[] arr, boolean[] visited, int curr) {
if (curr + m >= n || curr + 1 == n) {
return true;
}
boolean[] newVisited = new boolean[n];
for (int i = 0; i < n; i++) {
newVisited[i] = visited[i];
}
boolean s = false;
if (!visited[curr+1] && arr[curr+1] == 0) {
newVisited[curr+1] = true;
s = solve(n,m,arr,newVisited,curr+1);
}
if (s) {
return true;
}
if (m > 1 && arr[curr+m] == 0 && !visited[curr+m]) {
newVisited[curr+m] = true;
s = solve(n,m,arr,newVisited,curr+m);
}
if (s) {
return true;
}
if (curr > 0 && arr[curr-1] == 0 && !visited[curr-1]) {
newVisited[curr-1] = true;
s = solve(n,m,arr,newVisited,curr-1);
}
return s;
}
Your implementation does indeed seem to have exponential complexity. I did not really think about this part of your question. It is perhaps a bit tedious to come up with a worst case scenario. But one "at-least-pretty-bad" scenario would be to have the first n-m elements in arr set to 0 and the last m elements set to 1. A lot of branching right there, not really making use of the memoization mechanism. I would guess that your solution it is at least exponential in n/m.
Here is another solution. We can rephrase the problem as a graph one. Let the elements in your array be the vertices of a directed graph and let there be an edge in between every pair of vertices of one of the following forms: (x,x-1), (x,x+1) and (x,x+m), if both ends of such an edge have value 0. Add an additional vertex t to your graph. Also add an edge from every vertex with value 0 in {n-m+1,n-m+2,...,n} to t. So we have no more than 3n+m edges in our graph. Now, your problem is equivalent to determining if there is a path from vertex 0 to t in the graph we have just constructed. This can be achieved by running a Depth First Search starting from vertex 0, having complexity O(|E|), which in our case is O(n+m).
Coming back to your solution, you are doing pretty much the same thing (perhaps without realizing it). The only real difference is that you are copying the visited array into newVisited and thus never really using all that memoization :p So, just eliminate newVisited, use visited wherever you are using newVisited and check what happens.

Find minimum cost to convert array to arithmetic progression

I recently encountered this question in an interview. I couldn't really come up with an algorithm for this.
Given an array of unsorted integers, we have to find the minimum cost in which this array can be converted to an Arithmetic Progression where a cost of 1 unit is incurred if any element is changed in the array. Also, the value of the element ranges between (-inf,inf).
I sort of realised that DP can be used here, but I couldn't solve the equation. There were some constraints on the values, but I don't remember them. I am just looking for high level pseudo code.
EDIT
Here's a correct solution, unfortunately, while simple to understand it's not very efficient at O(n^3).
function costAP(arr) {
if(arr.length < 3) { return 0; }
var minCost = arr.length;
for(var i = 0; i < arr.length - 1; i++) {
for(var j = i + 1; j < arr.length; j++) {
var delta = (arr[j] - arr[i]) / (j - i);
var cost = 0;
for(var k = 0; k < arr.length; k++) {
if(k == i) { continue; }
if((arr[k] + delta * (i - k)) != arr[i]) { cost++; }
}
if(cost < minCost) { minCost = cost; }
}
}
return minCost;
}
Find the relative delta between every distinct pair of indices in the array
Use the relative delta to test the cost of transforming the whole array to AP using that delta
Return the minimum cost
Louis Ricci had the right basic idea of looking for the largest existing arithmetic progression, but assumed that it would have to appear in a single run, when in fact the elements of this progression can appear in any subset of the positions, e.g.:
1 42 3 69 5 1111 2222 8
requires just 4 changes:
42 69 1111 2222
1 3 5 8
To calculate this, notice that every AP has a rightmost element. We can suppose each element i of the input vector to be the rightmost AP position in turn, and for each such i consider all positions j to the left of i, determining the step size implied for each (i, j) combination and, when this is integer (indicating a valid AP), add one to the the number of elements that imply this step size and end at position i -- since all such elements belong to the same AP. The overall maximum is then the longest AP:
struct solution {
int len;
int pos;
int step;
};
solution longestArithProg(vector<int> const& v) {
solution best = { -1, 0, 0 };
for (int i = 1; i < v.size(); ++i) {
unordered_map<int, int> bestForStep;
for (int j = 0; j < i; ++j) {
int step = (v[i] - v[j]) / (i - j);
if (step * (i - j) == v[i] - v[j]) {
// This j gives an integer step size: record that j lies on this AP
int len = ++bestForStep[step];
if (len > best.len) {
best.len = len;
best.pos = i;
best.step = step;
}
}
}
}
++best.len; // We never counted the final element in the AP
return best;
}
The above C++ code uses O(n^2) time and O(n) space, since it loops over every pair of positions i and j, performing a single hash read and write for each. To answer the original problem:
int howManyChangesNeeded(vector<int> const& v) {
return v.size() - longestArithProg(v).len;
}
This problem has a simple geometric interpretation, which shows that it can be solved in O(n^2) time and probably can't be solved any faster than that (reduction from 3SUM). Suppose our array is [1, 2, 10, 3, 5]. We can write that array as a sequence of points
(0,1), (1,2), (2,10), (3,3), (4,5)
in which the x-value is the index of the array item and the y-value is the value of the array item. The question now becomes one of finding a line which passes the maximum possible number of points in that set. The cost of converting the array is the number of points not on a line, which is minimized when the number of points on a line is maximized.
A fairly definitive answer to that question is given in this SO posting: What is the most efficient algorithm to find a straight line that goes through most points?
The idea: for each point P in the set from left to right, find the line passing through that point and a maximum number of points to the right of P. (We don't need to look at points to the left of P because they would have been caught in an earlier iteration).
To find the maximum number of P-collinear points to the right of P, for each such point Q calculate the slope of the line segment PQ. Tally up the different slopes in a hash map. The slope which maps to the maximum number of hits is what you're looking for.
Technical issue: you probably don't want to use floating point arithmetic to calculate the slopes. On the other hand, if you use rational numbers, you potentially have to calculate the greatest common divisor in order to compare fractions by comparing numerator and denominator, which multiplies running time by a factor of log n. Instead, you should check equality of rational numbers a/b and c/d by testing whether ad == bc.
The SO posting referenced above gives a reduction from 3SUM, i.e., this problem is 3SUM-hard which shows that if this problem could be solved substantially faster than O(n^2), then 3SUM could also be solved substantially faster than O(n^2). This is where the condition that the integers are in (-inf,inf) comes in. If it is known that the integers are from a bounded set, the reduction from 3SUM is not definitive.
An interesting further question is whether the idea in the Wikipedia for solving 3SUM in O(n + N log N) time when the integers are in the bounded set (-N,N) can be used to solve the minimum cost to convert an array to an AP problem in time faster than O(n^2).
Given the array a = [a_1, a_2, ..., a_n] of unsorted integers, let diffs = [a_2-a_1, a_3-a_2, ..., a_n-a_(n-1)].
Find the maximum occurring value in diffs and adjust any values in a necessary so that all neighboring values differ by this amount.
Interestingly,even I had the same question in my campus recruitment test today.While doing the test itself,I realised that this logic of altering elements based on most frequent differences between 2 subsequent elements in the array fails in some cases.
Eg-4,5,8,9 .According to the logic of a2-a1,a3-a2 as proposed above,answer shud be 1 which is not the case.
As you suggested DP,I feel it can be on the lines of considering 2 values for each element in array-cost when it is modified as well as when it is not modified and return minimum of the 2.Finally terminate when you reach end of the array.

Dynamic programming: Find longest subsequence that is zig zag using only one dp array

Can this problem be done using only one dp array?
It is the zigzag problem from topcoder (http://community.topcoder.com/stat?c=problem_statement&pm=1259&rd=4493)
A sequence of numbers is called a zig-zag sequence if the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a zig-zag sequence.
For example, 1,7,4,9,2,5 is a zig-zag sequence because the differences (6,-3,5,-7,3) are alternately positive and negative. In contrast, 1,4,7,2,5 and 1,7,4,5,5 are not zig-zag sequences, the first because its first two differences are positive and the second because its last difference is zero.
Given a sequence of integers, sequence, return the length of the longest subsequence of sequence that is a zig-zag sequence. A subsequence is obtained by deleting some number of elements (possibly zero) from the original sequence, leaving the remaining elements in their original order.
For reference: the DP with two arrays uses an array A[1..n] where A[i] is the maximum length of a zig-zag sequence ending with a zig on element i, and an array B[1..n] where B[i] is the maximum length of a zig-zag sequence ending with a zag on element i. For i from 1 to n, this DP uses the previous entries of the A array to compute the B[i], and the previous entries of the B array to compute A[i]. At the cost of an extra loop, it would be possible to recreate the B entries on demand and thus use only the A array. I'm not sure if this solves your problem, though.
(Also, since the input arrays are so short, there are any number of encoding tricks not worth mentioning.)
Here's an attempt, I'm returning the indices from where you have zigzag. In your 2nd input (1,4,7,2,5), it returns indices of 5 and 4 since it's a zigzag from 4,7,2,5.
You can figure out if the whole array is zigzag based on the result.
public class LongestZigZag
{
private readonly int[] _input;
public LongestZigZag(int[] input)
{
_input = input;
}
public Tuple<int,int> Sequence()
{
var indices = new Tuple<int, int>(int.MinValue, int.MinValue);
if (_input.Length <= 2) return indices;
for (int i = 2; i < _input.Length; i++)
{
var firstDiff = _input[i - 1] - _input[i - 2];
var secondDiff = _input[i] - _input[i - 1];
if ((firstDiff > 0 && secondDiff < 0) || (firstDiff < 0 && secondDiff > 0))
{
var index1 = indices.Item1;
if (index1 == int.MinValue)
{
index1 = i - 2;
}
indices = new Tuple<int, int>(index1, i);
}
else
{
indices = new Tuple<int, int>(int.MinValue, int.MinValue);
}
}
return indices;
}
}
Dynamic Programming takes O(n2) time to run a program. I have designed a code which is of Linear Time Complexity O(n). With one go in the array, it gives the length of Largest possible sequence. I have tested for many test cases provided by different sites for the problem and have got positive results.
Here is my C implementation of code:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i,j;
int n;
int count=0;
int flag=0;
scanf(" %d",&n);
int *a;
a = (int*)malloc(n*sizeof(a));
for(i=0;i<n;i++)
{
scanf(" %d",&a[i]); //1,7,5,10,13,15,10,5,16,8
}
i=0;
if(a[0] < a[1])
{
count++;
while(a[i] <= a[i+1] && i<n-1)
i++;
if(i==n-1 && a[i-1]<a[i])
{
count++;
i++;
}
}
while(i<n-1)
{ count++;
while(a[i] >= a[i+1] && i<n-1)
{
i++;
}
if(i==n-1 && a[i-1]>a[i])
{
count++;
break;
}
if(i<n-1)
count++;
while(a[i] <= a[i+1] && i<n-1)
{
i++;
}
if(i==n-1 && a[i-1]<a[i])
{
count++;
break;
}
}
printf("%d",count);
return 0;
}
Every (to my knowledge on the topic, so don't take it for granted) solution which you work out with dynamic programming, comes down to representing a "solution space" (meaning every possible solution that is correct, not necessarily optimal) with a DAG (Directed Acyclic Graph).
For example, if you are looking for a longest rising subseqence, then the solution space can be represented as the following DAG:
Nodes are labeled with the numbers of the sequence
Edge e(u, v) between two nodes indicates that valueOf(u) < valueOf(v) (where valueOf(x) is the value associated with node x)
In dynamic programming, finding an optimal solution to the problem is the same thing as traversing this graph in the right way. The information provided by that graph is in some sense represented by that DP array.
In this case we have two ordering operations. If we would present both of them on one of such graphs, that graph would not be acyclic - we will require at least two graphs (one representing < relation, and one for >).
If the topological ordering requires two DAGs, the solution will require two DP arrays, or some clever way of indicating which edge in Your DAG corresponds to which ordering operation (which in my opinion needlessly complicates the problem).
Hence no, You can't do it with just one DP array. You will require at least two. At least if you want a simple solution that is approached purely by using dynamic programming.
The recursive call for this problem should look something like this (the directions of the relations might be wrong, I haven't checked it):
S - given sequence (array of integers)
P(i), Q(i) - length of the longest zigzag subsequence on elements S[0 -> i] inclusive (the longest sequence that is correct, where S[i] is the last element)
P(i) = {if i == 0 then 1
{max(Q(j) + 1 if A[i] < A[j] for every 0 <= j < i)
Q(i) = {if i == 0 then 0 #yields 0 because we are pedantic about "is zig the first relation, or is it zag?". If we aren't, then this can be a 1.
{max(P(j) + 1 if A[i] > A[j] for every 0 <= j < i)
This should be O(n) with the right memoization (two DP arrays). These calls return the length of the solution - the actual result can be found by storing "parent pointer" whenever a max value is found, and then traversing backwards on these pointers.

Optimizations for longest path problem in cyclic graph

What optimizations exist for trying to find the longest path in a cyclic graph?
Longest path in cyclic graphs is known to be NP-complete. What optimizations or heuristics can make finding the longest path faster than DFSing the entire graph? Are there any probabilistic approaches?
I have a graph with specific qualities, but I'm looking for an answer to this in the general case. Linking to papers would be fantastic. Here is a partial answer:
Confirm it is cyclic. Longest path in acyclic graphs is easily computed using dynamic programming.
Find out if the graph is planar (which algorithm is best?). If it is, you might see if it is a block graph, ptolemaic graph, or cacti graph and apply the methods found in this paper.
Find out how many simple cycles there are using Donald B Johnson's algorithm (Java implementation). You can change any cyclic graph into an acyclic one by removing an edge in a simple cycle. You can then run the dynamic programming solution found on the Wikipedia page. For completeness, you would have to do this N times for each cycle, where N is the length of the cycle. Thus, for an entire graph, the number of times you have to run the DP solution is equal to the product of the lengths of all cycles.
If you have to DFS the entire graph, you can prune some paths by computing the "reachability" of each node in advance. This reachability, which is mainly applicable to directed graphs, is the number of nodes each node can reach without repetitions. It is the maximum the longest path from that node could possibly be. With this information, if your current path plus the reachability of the child node is less than the longest you've already found, there is no point in taking that branch as it is impossible that you would find a longer path.
Here is a O(n*2^n) dynamic programming approach that should be feasible for up to say 20 vertices:
m(b, U) = the maximum length of any path ending at b and visiting only (some of) the vertices in U.
Initially, set m(b, {b}) = 0.
Then, m(b, U) = max value of m(x, U - x) + d(x, b) over all x in U such that x is not b and an edge (x, b) exists. Take the maximum of these values for all endpoints b, with U = V (the full set of vertices). That will be the maximum length of any path.
The following C code assumes a distance matrix in d[N][N]. If your graph is unweighted, you can change every read access to this array to the constant 1. A traceback showing an optimal sequence of vertices (there may be more than one) is also computed in the array p[N][NBITS].
#define N 20
#define NBITS (1 << N)
int d[N][N]; /* Assumed to be populated earlier. -1 means "no edge". */
int m[N][NBITS]; /* DP matrix. -2 means "unknown". */
int p[N][NBITS]; /* DP predecessor traceback matrix. */
/* Maximum distance for a path ending at vertex b, visiting only
vertices in visited. */
int subsolve(int b, unsigned visited) {
if (visited == (1 << b)) {
/* A single vertex */
p[b][visited] = -1;
return 0;
}
if (m[b][visited] == -2) {
/* Haven't solved this subproblem yet */
int best = -1, bestPred = -1;
unsigned i;
for (i = 0; i < N; ++i) {
if (i != b && ((visited >> i) & 1) && d[i][b] != -1) {
int x = subsolve(i, visited & ~(1 << b));
if (x != -1) {
x += d[i][b];
if (x > best) {
best = x;
bestPred = i;
}
}
}
}
m[b][visited] = best;
p[b][visited] = bestPred;
}
return m[b][visited];
}
/* Maximum path length for d[][].
n must be <= N.
*last will contain the last vertex in the path; use p[][] to trace back. */
int solve(int n, int *last) {
int b, i;
int best = -1;
/* Need to blank the DP and predecessor matrices */
for (b = 0; b < N; ++b) {
for (i = 0; i < NBITS; ++i) {
m[b][i] = -2;
p[b][i] = -2;
}
}
for (b = 0; b < n; ++b) {
int x = subsolve(b, (1 << n) - 1);
if (x > best) {
best = x;
*last = b;
}
}
return best;
}
On my PC, this solves a 20x20 complete graph with edge weights randomly chosen in the range [0, 1000) in about 7s and needs about 160Mb (half of that is for the predecessor trace).
(Please, no comments about using fixed-size arrays. Use malloc() (or better yet, C++ vector<int>) in a real program. I just wrote it this way so things would be clearer.)

Resources