Here is a very interesting problem which I have encountered: there is a directed tree in which the weight of each node changes with time and I have to find the distance from the root to some node.
Problem Statement:
There’s a long queue in front of a ticket counter. Here are the queue
considerations.
There can be max 2 incoming queues merging at a junction point
There can be only one outgoing queue from any junction point
There can be multiple junction points & the queues move
uni-directionally
There will be only one final ticket counter point to which all the
queues lead
There are multiple entry points for the fans to reach the
counter
I need to design a system that can suggest to the fans the
“Optimal Path” and its “Expected Time" to reach the counter
The expected time to reach the counter from a queue depends on the number of people in that queue plus the number of people in the other queues.
Time taken to cross the ticket counter and receive the ticket is 1
time unit
Assume that there is a policeman standing at each junction point
whose job is to open the junction gate to send people from the
in-queue(s) to the out-queue . If there are multiple in-queues for a
junction, the policeman will send fans from each queue one by one
alternatively
For example, if there are 2 in-queues containing 3 fans each, the leading person from queue1 will be sent in first, followed by the leading person from queue2, followed by next person from queue1 and so on. It’s an alternate pick between the incoming queues.
Full Problem Statement
For a Given Input
The first line contains the number of junctions
The second line contains the number of queues
The next 'e' lines contain three values: the start junction, the end
junction and the number of people on this queue. (This is also the maximum number of people that can stand in this queue.)
Calculate the minimum time for a person to reach the ticket counter who is just about to enter any queue. Also, output the path that he should take to reach the counter in minimum time in the worst case (at each junction point, the policeman starts choosing people from the in-queue other than the one we are calculating the minimum time for).
How can one solve this type of time-varying problem?
For Example:
7
6
1 5 9
2 5 5
3 6 1
4 6 3
5 7 7
6 7 4
Graph Looks Like This:
Ticket counter point: 7
Entry points: 1, 2, 3, 4
Time required for a person who is just entering the queue from entry
point 3: 1 person from queue(3,6) + 2 people from queue(4,6) + 4
people from queue(6,7) + 7 people from queue(5,7) + 1 person from
queue(1,5) will go before this person.
Optimal time = 15
Path is 3 -> 6 -> 7
This problem can be solved by finding the shortest path from each entry node (leaves) to the exit node (root).
In my implementation below I used an adjacency matrix to represent that kind of (directed) graph, but you can think of it as a binary tree (because the problem defined maximum 2 input queues for each junction).
Pseudo-code
int shortestPath(root)
if (both childs exists)
return min(shortestPath(node->left),shortestPath(node->right))*2+1
if (left child exists)
return shortestPath(node->left)
if (right child exists)
return shortestPath(node->right)
return 0; //no childs
The only difference between a normal shortest path and this problem is that whenever we have two incoming queues, the policeman sends fans from each queue one by one alternatively. Which means that in order to pass that queue it will take double the time +1. The +1 is because we assume that he starts from the longer queue path.
C++ Code
Here is a working C++ code that returns a pair with both the optimal time and its path. In case there are more than one optimal paths it will return only one of them.
const pair<int,vector<int>>& min(const pair<int,vector<int>>& a, const pair<int,vector<int>>& b) {
return (a.first < b.first) ? a : b;
}
pair<int,vector<int>> findShortestPath(vector<vector<int>>& graph, int v){
vector<pair<int,vector<int>>> childs;
for (int i=0; i<v; i++){
if (graph[i][v] != -1){
pair<int,vector<int>> path = findShortestPath(graph,i);
path.second.push_back(v+1);
childs.push_back(make_pair(path.first + graph[i][v], path.second));
}
}
if (childs.size() == 2){
pair<int,vector<int>> path = min(childs[0],childs[1]);
return make_pair(path.first*2+1, path.second);
}
if (childs.size() == 1){
return make_pair(childs[0].first,childs[0].second);
}
else{
vector<int> start = {v+1};
return make_pair(0,start);
}
}
The time complexity of this code is O(n^2) where n is the number of vertices. You might also be able to implement it in O(n) using adjacency list representation (= binary tree).
For completeness, here is also the main for creating the graph from the given input and printing the optimal time and path. See this live test of your example's input
int main()
{
int n, e;
cin >> n; //num of vertices
cin >> e; //num of queues
vector<vector<int>> graph;
//initialize graph matrix cells to -1
graph.resize(n);
for (int i=0;i<n;i++){
graph[i].resize(n);
for (int j=0;j<n;j++)
graph[i][j] = -1;
}
//add edges and their weights
for (int i=0;i<e;i++){
int s,d,val;
cin >> s >> d >> val;
graph[s-1][d-1] = val;
}
//run algorithm
pair<int,vector<int>> path = findShortestPath(graph, n-1);
//print results
cout << path.first << endl;
for (int i=0;i<path.second.size()-1;i++)
cout << path.second[i] << " -> ";
cout << path.second[path.second.size()-1] << endl;
return 0;
}
This should be solved by dynamic programming.
Let P(j) be the position of the person ,if it takes the optimal fan , to pass through the junction point j. For example P(6) = 4 in your case , because someone arriving at junction point 3 will be the 4th to pass through junction point 6 (after P27,P26 and P28).
The following three propositions are obvious and solve the problem.
If a "junction point" j is a fan , P(j) = 1 (base case)
If a "junction point" j is a proper junction point with children l and r , and there are x people in the queue between l and j and y people in the queue between r and j , we have P(j) = 2 * min( P(l) + x , P(r) + y)
If someone if the n'th to go through the counter, it takes n-1 time to get there.
You can get the time easily using DP and with some book keeping(if the min is achieved n the left or on the right) you can get which is the optimal fan.
Related
I appeared in an interview. I stuck in one question. I am asking the same.
Question: There is circular road is given. That road contains number
of petrol pumps. Each petrol pump have given amount of petrol.
Distance between each two consecutive petrol pump is also given. Now
there is a vehicle is given having empty fuel tank of limitless
capacity. Build an algorithm so that vehicle can cover complete round
without any backtracking. It is given that such path is definitely
possible.
Input: (int fuel[], int distance[])
Output: petrol pump index from where vehicle can make complete round of circular road.
My approaches:
Check from each petrol pump, if fuel tank is empty in between path, move to next petrol pump. and start the same process again. This algorithm takes O(N^2), here N = number of petrol pumps.
Then I move to the Binary search concept, to reduce the complexity to O(n*logn). But I failed to conclude the solution. I messed up in this solution.
Then I try to apply some intelligence, by choosing that petrol pump whose left petrol is maximum in between that two petrol pumps.
(This may be equivalent to the algorithm Evgeny Kluev posted, which I don't understand. If so, that answer has priority.)
Let F_i_j be the net, signed amount of fuel in the tank on arriving at j having started at i with zero fuel in the tank before filling at i.
Calculate F_0_i for every node i by working around the circle adding fuel at each node and subtracting the fuel cost of each edge.
If F_0_0, the net fuel at the end of a circuit starting at 0, is negative, then there is not enough fuel in the system (this is not supposed to happen according to the problem statement).
If no F_0_i is negative, report 0 as result.
Otherwise, find the node s with the most negative value of F_0_s. Pick s as the starting node.
For any node i, F_s_i is equal to F_0_i + abs(F_0_s). Since F_0_s is the most negative F_0_i, that makes F_s_i non-negative.
Worked example, as suggested in a comment by Handcraftsman:
Label nodes 0 through 4
node: 0,1,2,3,4
fuel: 1,3,4,4,1
dist: 1,4,2,2,2
First calculate F_0_i for i = 1, 2, 3, 4, 0
Start at node 0 with 0 fuel.
f_0_1 = 0 + 1 - 1 = 0, min score 0, node 1;
f_0_2 = 0 + 3 - 4 = -1, min score -1, node 2;
f_0_3 = -1 + 4 - 2 = 1;
f_0_4 = 1 + 4 - 2 = 3;
f_0_0 = 3 + 1 - 2 = 2;
f_0_0 is non-negative, so there is enough fuel.
The minimum score was -1, at node 2, so the result is node 2.
The bruteforce solution should be obvious. Start from every i, and check to see if every point is reachable with the gas available. Return the first i for which you can complete the trip without the gas reaching a negative number. This approach would however be quadratic. Lets look at how we can improve.
We can take greedy approach here.
1 If sum of gas is more than sum of cost, it imply that there wont be any solution. We can discard the point from which we started
2 Lets say you start at i, and hit first negative of sum(gas) - sum(cost) at j. We know TotalSum(gas) - TotalSum(cost) > 0. So we can discard i to j and start from j + 1.
Java code of above algo
public int canCompleteCircuit(final List<Integer> gas, final List<Integer> cost) {
int start = 0;
int totalGas = 0;
int totalCost = 0;
int currentCost = 0;
for (int i = 0; i < gas.size(); i++) {
totalCost += cost.get(i);
totalGas += gas.get(i);
currentCost += gas.get(i) - cost.get(i);
if (currentCost <0){
start = i + 1;
currentCost = 0;
}
}
if (totalCost > totalGas){
return -1;
}
return start;
}
I couldn't solve the problem without first understanding, when it is possible to find a correct course.
D - sum of all the distances
P - sum of all the fuel pumps
If P < D the problem is insolvable
If P = D it always has a solution, proof below
If P > D we can reduce some pumps and apply the solution as if P = D.
So we assume that P = D, that is we have exactly the amount of fuel needed to complete the course. Let's draw a chart, amount of fuel in tank as a function of time. If the tanking time is equal to 0, it is not a real function, but it's not important. We start anywhere and have some fuel in tank.
An example for pumps 1, 4, 3 and distances 2, 5, 1:
|\
| \
|\ | \ |\
\| \ |
\|
Notice things:
We may travel infinitely around and the chart will be repeated the same. We'll be back in the starting place with the same amount of fuel which we started with.
There will always be one point with the lowest fuel amount and it will be always the same station (if there are several minimal stations, it doesn't change anything)
If we start in a different place, the minimal point will always fall in the same station.
Based on the above we start in a minimal pump and assign 0 level to it. That is the proof that our fuel will never go below 0, that is we will be able to continue around and around and around...
Now we see that Evgeny's solution is correct, although calculation of S and S/N is unnecessary. We only need to add the pump value and subtract the distance, in Evgeny's algorithm it was V. So the optimal starting station is the one at which we arrive with minimal fuel contents.
It took me much time too understand why does Evgeny start in arbitrary direction. Why is he sure that the solution exists in the direction he took and not in the opposite? But when we know that P = D is a sufficient condition to complete the course, we also know that the direction is not important. We should pick a different station as a starting point in the opposite direction, but we can do it in both directions.
Nithis, how much time were you given? Could you say what was the company name? :) I think it was quite a difficult task, at least for a guy not familiar with similar circular problems.
This is a condition of recursion.First you check if you have fuel left for the last stretch.
If the fuel needed is x and the fuel in the pump n is y.If x>y then we need to have (x-y)+the fuel needed for the stretch between pumps n,n-1 else only the fuel required for the last but one stretch is need from the pump n-1.This continues till you reach pump 1.
public static void main(String[] args) {
int petrol[] = { 1, 3, 4, 4, 1 };
int distance[] = { 1, 4, 2, 2, 2 };
petrol = new int[] { 4, 6, 7, 4 };
distance = new int[] { 6, 5, 3, 5 };
List<PetrolPump> petrolPumps = PetrolPump.getAll(petrol, distance);
int start = 0;
int end = petrol.length;
int remainingPetrol = 0;
do {
remainingPetrol += petrolPumps.get(start).petrol - petrolPumps.get(start).distance;
if (remainingPetrol < 0) {
remainingPetrol = 0;
end = start;
}
start++;
if (start >= petrol.length) {
start = 0;
}
} while (start != end);
System.out.println(end + 1);
}
static class PetrolPump {
int petrol;
int distance;
PetrolPump(int petrol, int distance) {
this.petrol = petrol;
this.distance = distance;
}
static List<PetrolPump> getAll(int[] petrol, int[] distance) {
List<PetrolPump> petrolPumps = new ArrayList<PetrolPump>(distance.length);
for (int i = 0; i < distance.length; i++) {
petrolPumps.add(new PetrolPump(petrol[i], distance[i]));
}
return petrolPumps;
}
}
I came across this question from interviewstreet.com
Machines have once again attacked the kingdom of Xions. The kingdom
of Xions has N cities and N-1 bidirectional roads. The road network is
such that there is a unique path between any pair of cities.
Morpheus has the news that K Machines are planning to destroy the
whole kingdom. These Machines are initially living in K different
cities of the kingdom and anytime from now they can plan and launch an
attack. So he has asked Neo to destroy some of the roads to disrupt
the connection among Machines i.e after destroying those roads there
should not be any path between any two Machines.
Since the attack can be at any time from now, Neo has to do this task
as fast as possible. Each road in the kingdom takes certain time to
get destroyed and they can be destroyed only one at a time.
You need to write a program that tells Neo the minimum amount of time
he will require to disrupt the connection among machines.
Sample Input First line of the input contains two, space-separated
integers, N and K. Cities are numbered 0 to N-1. Then follow N-1
lines, each containing three, space-separated integers, x y z, which
means there is a bidirectional road connecting city x and city y, and
to destroy this road it takes z units of time. Then follow K lines
each containing an integer. Ith integer is the id of city in which ith
Machine is currently located.
Output Format Print in a single line the minimum time required to
disrupt the connection among Machines.
Sample Input
5 3
2 1 8
1 0 5
2 4 5
1 3 4
2
4
0
Sample Output
10
Explanation Neo can destroy the road connecting city 2 and city 4 of
weight 5 , and the road connecting city 0 and city 1 of weight 5. As
only one road can be destroyed at a time, the total minimum time taken
is 10 units of time. After destroying these roads none of the Machines
can reach other Machine via any path.
Constraints
2 <= N <= 100,000
2 <= K <= N
1 <= time to destroy a road <= 1000,000
Can someone give idea how to approach the solution.
The kingdom has N cities, N-1 edges and it's fully connected, therefore our kingdom is tree (in graph theory). At this picture you can see tree representation of your input graph in which Machines are represented by red vertices.
By the way you should consider all paths from the root vertex to all leaf nodes. So in every path you would have several red nodes and during removing edges you should take in account only neighboring red nodes. For example in path 0-10 there are two meaningfull pairs - (0,3) and (3,10). And you must remove exactly one node (not less, not more) from each path which connected vertices in pairs.
I hope this advice was helpful.
All the three answers will lead to correct solution but you can not achieve the solution within the time limit provided by interviewstreet.com. You have to think of some simple approach to solve this problem successfully.
HINT: start from the node where machine is present.
As said by others, a connected graph with N vertices and N-1 edges is a tree.
This kind of problem asks for a greedy solution; I'd go for a modification of Kruskal's algorithm:
Start with a set of N components - 1 for every node (city). Keep track of which components contain a machine-occupied city.
Take 1 edge (road) at a time, order by descending weight (starting with roads most costly to destroy). For this edge (which necessarily connects two components - the graph is a tree):
if both neigboring components contain a machine-occupied city, this road must be destroyed, mark it as such
otherwise, merge the neigboring components into one. If one of them contained a machine-occupied city, so does the merged component.
When you're done with all edges, return the sum of costs for the destroyed roads.
Complexity will be the same as Kruskal's algorithm, that is, almost linear for well chosen data structure and sorting method.
pjotr has a correct answer (though not quite asymptotically optimal), but this statement
This kind of problem asks for a greedy solution
really requires proof, as in the real world (as distinguished from competitive programming), there are several problems of this “kind” for which the greedy solution is not optimal (e.g., this very same problem in general graphs, which is called multiterminal cut and is NP-hard). In this case, proof consists of verifying the matroid axioms. Let a set of edges A ⊆ E be independent if the graph (V, E ∖ A) has exactly |A| + 1 connected components containing at least one machine.
Independence of the empty set. Trivial.
Hereditary property. Let A be an independent set. Every edge e ∈ A joins two connected components of the graph (V, E ∖ A), and every connected component contains at least one machine. In putting e back in the graph, the number of connected components containing at least one machine decreases by 1, so A ∖ {e} is also independent.
Augmentation property. Let A and B be independent sets with |A| < |B|. Since (V, E ∖ B) has more connected components than (V, E ∖ A), there exists by the pigeonhole principle a pair of machines u, v such that u and v are disconnected by B but not by A. Since there is exactly one path from u to v, B contains at least one edge e on this path, and A cannot contain e. The removal of A ∪ {e} induces one more connected component containing at least one machine than A, so A ∪ {e} is independent, as required.
Start performing a DFS from either of the machine nodes. Also, keep track of the edge with min weight encountered so far. As soon as you find the next node which also contains a machine, delete the min edge recorded so far. Start DFS from this new node now.
Repeat until you have found all nodes where the machines exists.
Should be of the O(N) that way !!
I write some code, and pasted all the tests.
#include <iostream>
#include<algorithm>
using namespace std;
class Line {
public:
Line(){
begin=0;end=0; weight=0;
}
int begin;int end;int weight;
bool operator<(const Line& _l)const {
return weight>_l.weight;
}
};
class Point{
public:
Point(){
pre=0;machine=false;
}
int pre;
bool machine;
};
void DP_Matrix();
void outputLines(Line* lines,Point* points,int N);
int main() {
DP_Matrix();
system("pause");
return 0;
}
int FMSFind(Point* trees,int x){
int r=x;
while(trees[r].pre!=r)
r=trees[r].pre;
int i=x;int j;
while(i!=r) {
j=trees[i].pre;
trees[i].pre=r;
i=j;
}
return r;
}
void DP_Matrix(){
int N,K,machine_index;scanf("%d%d",&N,&K);
Line* lines=new Line[100000];
Point* points=new Point[100000];
N--;
for(int i=0;i<N;i++) {
scanf("%d%d%d",&lines[i].begin,&lines[i].end,&lines[i].weight);
points[i].pre=i;
}
points[N].pre=N;
for(int i=0;i<K;i++) {
scanf("%d",&machine_index);
points[machine_index].machine=true;
}
long long finalRes=0;
for(int i=0;i<N;i++) {
int bP=FMSFind(points,lines[i].begin);
int eP=FMSFind(points,lines[i].end);
if(points[bP].machine&&points[eP].machine){
finalRes+=lines[i].weight;
}
else{
points[bP].pre=eP;
points[eP].machine=points[bP].machine||points[eP].machine;
points[bP].machine=points[eP].machine;
}
}
cout<<finalRes<<endl;
delete[] lines;
delete[] points;
}
void outputLines(Line* lines,Point* points,int N){
printf("\nLines:\n");
for(int i=0;i<N;i++){
printf("%d\t%d\t%d\n",lines[i].begin,lines[i].end,lines[i].weight);
}
printf("\nPoints:\n");
for(int i=0;i<=N;i++){
printf("%d\t%d\t%d\n",i,points[i].machine,points[i].pre);
}
}
Alice and Bob play the following game:
1) They choose a permutation of the first N numbers to begin with.
2) They play alternately and Alice plays first.
3) In a turn, they can remove any one remaining number from the permutation.
4) The game ends when the remaining numbers form an increasing sequence. The person who played the last turn (after which the sequence becomes increasing) wins the game.
Assuming both play optimally, who wins the game?
Input:
The first line contains the number of test cases T. T test cases follow. Each case contains an integer N on the first line, followed by a permutation of the integers 1..N on the second line.
Output:
Output T lines, one for each test case, containing "Alice" if Alice wins the game and "Bob" otherwise.
Sample Input:
2
3
1 3 2
5
5 3 2 1 4
Sample Output:
Alice
Bob
Constraints:
1 <= T <= 100
2 <= N <= 15
The permutation will not be an increasing sequence initially.
I am trying to solve above problem. I have derived till far but I am stuck at a point. Please help me to proceed further.
In above problem, for permutation of length 2, player 1 always wins.
For a permutation of length 3, player 2 wins if the string is strictly increasing or decreasing.
For a permutation of length 4, If player 1 is able to make the string strictly increasing or decreasing by removing a character, she wins else player 2 wins.
Hence a conclusion is:
If current player is able to make the string strictly increasing he/she wins. (Trivial case)
If he/she is able to make it strictly decreasing the the winner is decided by the number of elements in that sequence. If there are even number of elements in that sequence, current player looses, else wins.
But what should be done if the resultant string is neither increasing nor decreasing??
This is a typical game problem. You have 2^15 possible positions which denote which are the remaining numbers. From the number of the remaining numbers you can derive whose turn it is. So now you have a graph that is defined in the following manner - the vertices are the possible sets of remaining numbers and there is an edge connecting two vertices u and v iff there is a move that changes set u to set v(i.e. set v has exactly one number less).
Now you already pointed out for which positions you know who is the winner straight away - the ones that represent increasing sequences of numbers this positions are marked as loosing. For all other positions you determine if they are wining or loosing in the following manner: a position is winning iff there is an edge connecting it to a loosing position. So all that is left is to something like a dfs with memoization and you can determine which positions are winning and which are loosing. As the graph is relatively small (2^15 vertices) this solution should be fast enough.
Hope this helps.
Of course, this can be done with "brute force" for small N, but don't you suspect an easier answer around inversions and the sign of a permutation?
Originally I suspected an answer like "Alice wins iff the sign is -1, else loses", but this is not the case.
But I would like to propose a representation of the problem that not only your algorithm may use, but one that will equally boost your paper-and-pen performance in this game.
An inversion is a pair of indices i<j such that a[i]>a[j]. Consider (i,j) an edge of an undirected graph with vertices 1,...,N. Each player deletes a vertex from this graph and wins if there are no edges left.
For 5 3 2 1 4, the resulting graph is
5--3
/|\ |
/ | \|
4 1--2
and Alice quickly sees that removing "5" gives Bob the opportunity to remove 2. Then no inversions are left, and Bob wins.
This game can be solved recursively.
Each time alice takes her first pick and picks i, subtract 1 from all the remaining numbers that are larger than i. Now we have the same game but with the numbers 1 to N-1
lets say your sequence is
1,3,5,4,2
on her first move, Alice can pick any number.
case1:
she picks 1, alice can win if bob cant win with 3,5,4,2 (equivalently 2,4,3,1)
case2:
she picks 3 first. Alice can win if bob cant win with 1,5,4,2 (equivalently 1,4,3,2)
case3:
she picks 5 first. Alice can win if bob cant win with 1,3,4,2
you get the idea.
So you can make a recursive function to work out the solution for a size N permutation all by using size N-1 permutations for each possible first guess. the base case for the recursion is when you have an in-order sequence.
Each step of the recursion, the person tries all possibilities and picks any that makes them win.
Because there are many combinations of moves that can get down to the same sequence, the recursion has overlapping sub problems. This means we can use dynamic programming, or simply "memoize" our function, greatly increasing efficiency.
For further speedup one may use symmetry in the permutations, as many groups of permutations are equivalent, such as the reverse of one permutation would yield the same result.
Good luck.
#tiwo ,#rup COnsidering 5 3 2 1 4 is the sequence first alice removes 5 and the bob removes 2 then the sequence is 3 1 4 which is not in increasing order then alice gets the chance to remove 1 and the the sequence is in ascending order Alice should be the answer. In the graph you gave there should be an edge between 3 and 1 as 1 and 3 are in inversion.
Please tell me where i am wrong as the answer given in the problem is infact BOB
You can solve it with minimax algorithm. Here is the code in java
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class Solution {
public static Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
int t = ni();
for(int i=0; i<t; i++){
int n = ni();
Map<Long, Boolean> map = new HashMap<Long, Boolean>();
int[] numbers = new int[n];
for(int j=0; j<n; j++){
numbers[j] = ni();
}
if(aliceWin(numbers, map)) System.out.println("Alice");
else System.out.println("Bob");
}
}
public static boolean aliceWin(int[] a, Map<Long, Boolean> map){
long h = hashCode(a); int temp;
if(map.containsKey(h)) return true;
for(int i=0; i<a.length; i++){
if(a[i]>0){
temp = a[i] ;
a[i] = 0;
if(isIncreasing(a)){
map.put(h, true);
a[i] = temp;
return true;
}
if(!aliceWin(a, map)) {
map.put(h, true);
a[i] = temp;
return true;
}
a[i] = temp;
}
}
return false;
}
public static long hashCode(int[] a){
long result = 0;
for(int i=0; i<a.length; i++){
result = (result << 4) + a[i];
}
return result;
}
public static boolean isIncreasing(int[] a){
int last = 0;
for(int i=0; i<a.length; i++){
if (a[i] > 0){
if(last > a[i]) return false;
last = a[i];
}
}
return true;
}
public static int ni(){
return sc.nextInt();
}
public static void print(Object... args){
System.out.println(Arrays.deepToString(args));
}
}
From blog: hackerrank-permutation-game
Here is some code that builds the graph for you, but requires you to call reverse() on the graph, create a source node connecting to all nodes in the base, flow back to source seeing if there is a way alice wins.
input_ = """2
3
1 3 2
5
5 3 2 1 4""".splitlines()
perms = [map(int,perm.split()) for perm in input_ if len(perm)>1]
"[['1', '3', '2'], ['5', '3', '2', '1', '4']]"
if networkx is None:
import networkx
from itertools import combinations
def build_graph(perm):
base = set()
G = networkx.DiGraph()
for r in range(1,len(perm)+1):
for combo in combinations(perm,r):
combo = list(combo)
if combo == sorted(combo):
base.add(tuple(combo))
continue
for i in range(r):
G.add_edge(tuple(combo),tuple(combo[:i]+combo[i+1:])) #you may want to reverse the graph later to point from base to source.
return G,base
def solve(G,base):
#dfs,
pass
for perm in perms:
G,base = build_graph(perms[0])
print solve(G,base)
can't we just check at each step that..
does a single change by the next player makes the sequence sorted.. if yes then make some other move..
or carry on with the move
like
5 3 2 1 4
if alice does 3 2 1 4
bob cannot win in a single turn by eliminating any...
like if
he does 2 1 4 it is nt sorted..
he does 3 1 4 it is nt sorted..
he does 3 2 4 it is nt sorted..
so 5 3 2 1 4 -> 3 2 1 4 is a valid move!!
now is bob's turn..
he'll check the same..
but in some time..there won't be a number such that u can make a move as above..
so u'll have to make a random move and who will win then can be easily calculated by the number of steps tht will make the sequence into single element!!
To me (using almost your own words):
If he/she is able to make it strictly increasing on the first move he/she wins (Trivial case) otherwise the the winner is decided by the number of elements in that sequence.
Take your second case as example.
I think that the graph solution is fine but it forgets that the players play in a optimal way. So don't need to check all the different path since some of them will derive from a non-optimal choice.
In short, I need a fast algorithm to count how many acyclic paths are there in a simple directed graph.
By simple graph I mean one without self loops or multiple edges.
A path can start from any node and must end on a node that has no outgoing edges. A path is acyclic if no edge occurs twice in it.
My graphs (empirical datasets) have only between 20-160 nodes, however, some of them have many cycles in them, therefore there will be a very large number of paths, and my naive approach is simply not fast enough for some of the graph I have.
What I'm doing currently is "descending" along all possible edges using a recursive function, while keeping track of which nodes I have already visited (and avoiding them). The fastest solution I have so far was written in C++, and uses std::bitset argument in the recursive function to keep track of which nodes were already visited (visited nodes are marked by bit 1). This program runs on the sample dataset in 1-2 minutes (depending on computer speed). With other datasets it takes more than a day to run, or apparently much longer.
The sample dataset: http://pastie.org/1763781
(each line is an edge-pair)
Solution for the sample dataset (first number is the node I'm starting from, second number is the path-count starting from that node, last number is the total path count):
http://pastie.org/1763790
Please let me know if you have ideas about algorithms with a better complexity. I'm also interested in approximate solutions (estimating the number of paths with some Monte Carlo approach). Eventually I'll also want to measure the average path length.
Edit: also posted on MathOverflow under same title, as it might be more relevant there. Hope this is not against the rules. Can't link as site won't allow more than 2 links ...
This is #P-complete, it seems. (ref http://www.maths.uq.edu.au/~kroese/ps/robkro_rev.pdf). The link has an approximation
If you can relax the simple path requirement, you can efficiently count the number of paths using a modified version of Floyd-Warshall or graph exponentiation as well. See All pairs all paths on a graph
As mentioned by spinning_plate, this problem is #P-complete so start looking for your aproximations :). I really like the #P-completeness proof for this problem, so I'd think it would be nice to share it:
Let N be the number of paths (starting at s) in the graph and p_k be the number of paths of length k. We have:
N = p_1 + p_2 + ... + p_n
Now build a second graph by changing every edge to a pair of paralel edges.For each path of length k there will now be k^2 paths so:
N_2 = p_1*2 + p_2*4 + ... + p_n*(2^n)
Repeating this process, but with i edges instead of 2, up n, would give us a linear system (with a Vandermonde matrix) allowing us to find p_1, ..., p_n.
N_i = p_1*i + p_2*(i^2) + ...
Therefore, finding the number of paths in the graph is just as hard as finding the number of paths of a certain length. In particular, p_n is the number of Hamiltonian Paths (starting at s), a bona-fide #P-complete problem.
I havent done the math I'd also guess that a similar process should be able to prove that just calculating average length is also hard.
Note: most times this problem is discussed the paths start from a single edge and stop wherever. This is the opposite from your problem, but you they should be equivalent by just reversing all the edges.
Importance of Problem Statement
It is unclear what is being counted.
Is the starting node set all nodes for which there is at least one outgoing edge, or is there a particular starting node criteria?
Is the the ending node set the set of all nodes for which there are zero outgoing edges, or can any node for which there is at least one incoming edge be a possible ending node?
Define your problem so that there are no ambiguities.
Estimation
Estimations can be off by orders of magnitude when designed for randomly constructed directed graphs and the graph is very statistically skewed or systematic in its construction. This is typical of all estimation processes, but particularly pronounced in graphs because of their exponential pattern complexity potential.
Two Optimizing Points
The std::bitset model will be slower than bool values for most processor architectures because of the instruction set mechanics of testing a bit at a particular bit offset. The bitset is more useful when memory footprint, not speed is the critical factor.
Eliminating cases or reducing via deductions is important. For instance, if there are nodes for which there is only one outgoing edge, one can calculate the number of paths without it and add to the number of paths in the sub-graph the number of paths from the node from which it points.
Resorting to Clusters
The problem can be executed on a cluster by distributing according to starting node. Some problems simply require super-computing. If you have 1,000,000 starting nodes and 10 processors, you can place 100,000 starting node cases on each processor. The above case eliminations and reductions should be done prior to distributing cases.
A Typical Depth First Recursion and How to Optimize It
Here is a small program that provides a basic depth first, acyclic traversal from any node to any node, which can be altered, placed in a loop, or distributed. The list can be placed into a static native array by using a template with a size as one parameter if the maximum data set size is known, which reduces iteration and indexing times.
#include <iostream>
#include <list>
class DirectedGraph {
private:
int miNodes;
std::list<int> * mnpEdges;
bool * mpVisitedFlags;
private:
void initAlreadyVisited() {
for (int i = 0; i < miNodes; ++ i)
mpVisitedFlags[i] = false;
}
void recurse(int iCurrent, int iDestination,
int path[], int index,
std::list<std::list<int> *> * pnai) {
mpVisitedFlags[iCurrent] = true;
path[index ++] = iCurrent;
if (iCurrent == iDestination) {
auto pni = new std::list<int>;
for (int i = 0; i < index; ++ i)
pni->push_back(path[i]);
pnai->push_back(pni);
} else {
auto it = mnpEdges[iCurrent].begin();
auto itBeyond = mnpEdges[iCurrent].end();
while (it != itBeyond) {
if (! mpVisitedFlags[* it])
recurse(* it, iDestination,
path, index, pnai);
++ it;
}
}
-- index;
mpVisitedFlags[iCurrent] = false;
}
public:
DirectedGraph(int iNodes) {
miNodes = iNodes;
mnpEdges = new std::list<int>[iNodes];
mpVisitedFlags = new bool[iNodes];
}
~DirectedGraph() {
delete mpVisitedFlags;
}
void addEdge(int u, int v) {
mnpEdges[u].push_back(v);
}
std::list<std::list<int> *> * findPaths(int iStart,
int iDestination) {
initAlreadyVisited();
auto path = new int[miNodes];
auto pnpi = new std::list<std::list<int> *>();
recurse(iStart, iDestination, path, 0, pnpi);
delete path;
return pnpi;
}
};
int main() {
DirectedGraph dg(5);
dg.addEdge(0, 1);
dg.addEdge(0, 2);
dg.addEdge(0, 3);
dg.addEdge(1, 3);
dg.addEdge(1, 4);
dg.addEdge(2, 0);
dg.addEdge(2, 1);
dg.addEdge(4, 1);
dg.addEdge(4, 3);
int startingNode = 0;
int destinationNode = 1;
auto pnai = dg.findPaths(startingNode, destinationNode);
std::cout
<< "Unique paths from "
<< startingNode
<< " to "
<< destinationNode
<< std::endl
<< std::endl;
bool bFirst;
std::list<int> * pi;
auto it = pnai->begin();
auto itBeyond = pnai->end();
std::list<int>::iterator itInner;
std::list<int>::iterator itInnerBeyond;
while (it != itBeyond) {
bFirst = true;
pi = * it ++;
itInner = pi->begin();
itInnerBeyond = pi->end();
while (itInner != itInnerBeyond) {
if (bFirst)
bFirst = false;
else
std::cout << ' ';
std::cout << (* itInner ++);
}
std::cout << std::endl;
delete pi;
}
delete pnai;
return 0;
}
How to find all chordless cycles in an undirected graph?
For example, given the graph
0 --- 1
| | \
| | \
4 --- 3 - 2
the algorithm should return 1-2-3 and 0-1-3-4, but never 0-1-2-3-4.
(Note: [1] This question is not the same as small cycle finding in a planar graph because the graph is not necessarily planar. [2] I have read the paper Generating all cycles, chordless cycles, and Hamiltonian cycles with the principle of exclusion but I don't understand what they're doing :). [3] I have tried CYPATH but the program only gives the count, algorithm EnumChordlessPath in readme.txt has significant typos, and the C code is a mess. [4] I am not trying to find an arbitrary set of fundametal cycles. Cycle basis can have chords.)
Assign numbers to nodes from 1 to n.
Pick the node number 1. Call it 'A'.
Enumerate pairs of links coming out of 'A'.
Pick one. Let's call the adjacent nodes 'B' and 'C' with B less than C.
If B and C are connected, then output the cycle ABC, return to step 3 and pick a different pair.
If B and C are not connected:
Enumerate all nodes connected to B. Suppose it's connected to D, E, and F. Create a list of vectors CABD, CABE, CABF. For each of these:
if the last node is connected to any internal node except C and B, discard the vector
if the last node is connected to C, output and discard
if it's not connected to either, create a new list of vectors, appending all nodes to which the last node is connected.
Repeat until you run out of vectors.
Repeat steps 3-5 with all pairs.
Remove node 1 and all links that lead to it. Pick the next node and go back to step 2.
Edit: and you can do away with one nested loop.
This seems to work at the first sight, there may be bugs, but you should get the idea:
void chordless_cycles(int* adjacency, int dim)
{
for(int i=0; i<dim-2; i++)
{
for(int j=i+1; j<dim-1; j++)
{
if(!adjacency[i+j*dim])
continue;
list<vector<int> > candidates;
for(int k=j+1; k<dim; k++)
{
if(!adjacency[i+k*dim])
continue;
if(adjacency[j+k*dim])
{
cout << i+1 << " " << j+1 << " " << k+1 << endl;
continue;
}
vector<int> v;
v.resize(3);
v[0]=j;
v[1]=i;
v[2]=k;
candidates.push_back(v);
}
while(!candidates.empty())
{
vector<int> v = candidates.front();
candidates.pop_front();
int k = v.back();
for(int m=i+1; m<dim; m++)
{
if(find(v.begin(), v.end(), m) != v.end())
continue;
if(!adjacency[m+k*dim])
continue;
bool chord = false;
int n;
for(n=1; n<v.size()-1; n++)
if(adjacency[m+v[n]*dim])
chord = true;
if(chord)
continue;
if(adjacency[m+j*dim])
{
for(n=0; n<v.size(); n++)
cout<<v[n]+1<<" ";
cout<<m+1<<endl;
continue;
}
vector<int> w = v;
w.push_back(m);
candidates.push_back(w);
}
}
}
}
}
#aioobe has a point. Just find all the cycles and then exclude the non-chordless ones. This may be too inefficient, but the search space can be pruned along the way to reduce the inefficiencies. Here is a general algorithm:
void printChordlessCycles( ChordlessCycle path) {
System.out.println( path.toString() );
for( Node n : path.lastNode().neighbors() ) {
if( path.canAdd( n) ) {
path.add( n);
printChordlessCycles( path);
path.remove( n);
}
}
}
Graph g = loadGraph(...);
ChordlessCycle p = new ChordlessCycle();
for( Node n : g.getNodes()) {
p.add(n);
printChordlessCycles( p);
p.remove( n);
}
class ChordlessCycle {
private CountedSet<Node> connected_nodes;
private List<Node> path;
...
public void add( Node n) {
for( Node neighbor : n.getNeighbors() ) {
connected_nodes.increment( neighbor);
}
path.add( n);
}
public void remove( Node n) {
for( Node neighbor : n.getNeighbors() ) {
connected_nodes.decrement( neighbor);
}
path.remove( n);
}
public boolean canAdd( Node n) {
return (connected_nodes.getCount( n) == 0);
}
}
Just a thought:
Let's say you are enumerating cycles on your example graph and you are starting from node 0.
If you do a breadth-first search for each given edge, e.g. 0 - 1, you reach a fork at 1. Then the cycles that reach 0 again first are chordless, and the rest are not and can be eliminated... at least I think this is the case.
Could you use an approach like this? Or is there a counterexample?
How about this. First, reduce the problem to finding all chordless cycles that pass through a given vertex A. Once you've found all of those, you can remove A from the graph, and repeat with another point until there's nothing left.
And how to find all the chordless cycles that pass through vertex A? Reduce this to finding all chordless paths from B to A, given a list of permitted vertices, and search either breadth-first or depth-first. Note that when iterating over the vertices reachable (in one step) from B, when you choose one of them you must remove all of the others from the list of permitted vertices (take special care when B=A, so as not to eliminate three-edge paths).
Find all cycles.
Definition of a chordless cycle is a set of points in which a subset cycle of those points don't exist. So, once you have all cycles problem is simply to eliminate cycles which do have a subset cycle.
For efficiency, for each cycle you find, loop through all existing cycles and verify that it is not a subset of another cycle or vice versa, and if so, eliminate the larger cycle.
Beyond that, only difficulty is figuring out how to write an algorithm that determines if a set is a subset of another.