Tuesday, 14 June 2016

Caesar's Legions : A game of Horsemen and Footmen (DP)

Pretty Awesome DP question (and basic :D )


Question :


Gaius Julius Caesar, a famous general, loved to line up his soldiers. Overall the army had n1 footmen and n2 horsemen. Caesar thought that an arrangement is not beautiful if somewhere in the line there are strictly more that k1 footmen standing successively one after another, or there are strictly more than k2 horsemen standing successively one after another. Find the number of beautiful arrangements of the soldiers.
Note that all n1 + n2 warriors should be present at each arrangement. All footmen are considered indistinguishable among themselves. Similarly, all horsemen are considered indistinguishable among themselves.
Input
The only line contains four space-separated integers n1n2k1k2 (1 ≤ n1, n2 ≤ 100, 1 ≤ k1, k2 ≤ 10) which represent how many footmen and horsemen there are and the largest acceptable number of footmen and horsemen standing in succession, correspondingly.
Output
Print the number of beautiful arrangements of the army modulo 100000000 (108). That is, print the number of such ways to line up the soldiers, that no more than k1 footmen stand successively, and no more than k2 horsemen stand successively.

input
2 1 1 10
output
1
input
2 3 1 2
output
5

HOW TO CRACK THIS QUESTION :


I think this problem was easier than C, to be honest. K1 and K2 were very low (<= 10) . I am not sure why. But there is a dp solution.

Let us say that we have r1 footmen and r2 horsemen left to place. The previous lastn positions, contain footmen or horsemen depending of the value of a variable lasttype, if lasttype = 1, then the previous lastn positions contain horsemen. So, we in fact remember the contents of the previous positions, we just have to make sure that lastn does not ever exceed k1 or k2 depending on whether they are footmen or horsemen.

Let f(r1,r2,lasttype, lastn) the total number of ways to solve that sub-problem. The final result will be f(n1,n2, 0,0) (There are n1 footmen to place, n2 horsemen, and we can pretend that there are 0 footmen in the previous positions). So, let us think of transitions:

* At each position, we can place a footman, or a horseman. Depending on what we do, we increase lastn or set (lastype = 1, lastn = 1) in case we placed a different type of unit than the previous one.

const int MOD = 100000000;
int n1, n2, k1, k2;

int mem[101][101][2][11];

int count(int r1, int r2, int lasttype, int lastn) {
int & c = mem[r1][r2][lasttype][lastn];

if (c == -1) {
c = 0;
// place r1
if (r1 > 0) {
if (lasttype == 1) {
c += count(r1-1,r2,0,1);
} else if (lastn < k1) {
c += count(r1-1,r2,0,lastn+1);
}
}
//place r2
if (r2 > 0) {
if (lasttype == 0) {
c += count(r1,r2-1,1,1);
} else if (lastn < k2) {
c += count(r1,r2-1,1,lastn+1);
}
c %= MOD;
}

if (r1 == 0 && r2 == 0) {
//all done!
c = 1;
}

}
return c;
}

int solve()
{
memset(mem,-1,sizeof(mem));

return count(n1, n2, 0,0);
}


Wednesday, 27 January 2016

Basic Question of Dyanmic Programming : Matrix Chain Multiplication

Matrix Multiplication

A quick dropback on what matrix multiplication is (although you should know it already :P ) :






M11 = r11× t11  +  r12× t21  +   r13×t31
M12 = r21× t11  +  r22× t21   +  r23×t31



  So basically given two matrices of p x q and r x s. Then they can only be multiplied iff q=r.  The dimension of resultant matrix C is p x s.  

The time to compute the matrix C is O(p*q*r) because we multiply q elements of each row of A with q elements of each column of B. This takes q time. One row of A is multiplied with each column of B this takes q*s time. As the number of rows in A is p, q*s will takes place p times. Hence the total complexity is p*q*s.


MATRIX CHAIN MULTIPLICATION :


What the hell is this "chain"?


lets say there are n matrices. The dimension of ith matrix is p(i-1) x p(i) (except for the first one). Its something like this :

1st :  p0 x p1
2nd : p1 x p2
3rd : p2 x p3
4th : p3 x p4
                                                                        .
                                                                        .
                                                                        .

You get the basic idea.

So observe that you can multiply any ith matrix with (i+1)th matrix because the row of ith is same as column of (i+1)th.
Matrix Chain multiplication is multiplying these matrices to give the final matrix of dimension p1 x pn.

M1 x M2 x M3 x M4... 

As only two matrices can be multiplied at one time, there can be many combinations in which these can be multiplied. Like if we have 4 matrices, there can be these combinations :

( M1( M2( M3M4 ) ) )
( ( M1( M2M3) ) M4 )
( M1 ( (M2M3) M4) )
( (M1M2) (M3M4) ) 

The cost of computation of each of these combinations will be different and obviously we want the minimum cost.

The number of combinations can be computed as such :

P(n)    = 1                                              , if n=1
        = summation of ( P(k)P(n-k) )      k=[1,n-1]     ,if n != 1

If we compute all the combinations of Parentheses, the complexity would be appox. O(2^n) which would take billions of years to run for large values of n.


So Here's where Dynamic Programming kicks in like a boss!


The 2 conditions of DP is Optimal substructures and Overlapping subproblems.
Now keeping this in mind, we approach using DP.

0. To Calculate Ai...j, optimally we divide it into 2 subproblems and recursively find the solution.

1. Consider a range from i to j, say A i...j. Now if we break it into 2 matrices and then multiply, i.e. first compute A i...k and A k+1...j. Then multiply them to get A i...j.

Ai..j = (A i..k) * (A k+1...j)

2. For some value of k between i and j-1, cost of one of the Ai...k * A k+1...j will be minimum. To find that min, we calculate the value for each k from i to j-1.


3. Now finding Ai..k is a subproblem and can similarly be found by recursion as we found    A i...j, by breaking into parts Ai..p and Ap+1...k and finding the minimum of the possibilities.

Recursify it !

 
So if we say m[i..j] stores the minimum of A i..j then we can say :

m[i..j] = m[i..k] + m[k+1..j] + p (i-1)*p (k)*p(j)

Store all the k's for each pair of i..j in s[i..j].

So simple! isn't it? :P

 
 
 Simple code of the recursive function :

 void func(int l[], int i, int j ) {
        
              if(i==j) return 0;    // Multiplying a matrix with itself
              int mn = INF;                   

              for ( int c = i+1 , c < j, c++ ) {
                     mn = min ( func (l,i,c)   +                              func (l,c+1 , j) +                               p [i-1] * p [c] * p [j] ,
                                mn ) ;
              }  
             return mn;
       }


BUT same subproblems are being computed over and over again! As can be seen in the diagram above. Computing same over and over again increases complexity. We can cache the subproblems when we solve them to use again later with recomputing or use bottom up DP like this :

int MatrixChainOrder(int p[], int n)
{
 
    /* For simplicity of the program, one extra row and one extra column are
       allocated in m[][].  0th row and 0th column of m[][] are not used */
    int m[n][n];
 
    int i, j, k, L, q;
 
    /* m[i,j] = Minimum number of scalar multiplications needed to compute
       the matrix A[i]A[i+1]...A[j] = A[i..j] where dimention of A[i] is
       p[i-1] x p[i] */
 
    // cost is zero when multiplying one matrix.
    for (i = 1; i < n; i++)
        m[i][i] = 0;
 
    // L is chain length. 
    for (L=2; L<n; L++)  
    {
        for (i=1; i<=n-L+1; i++)
        {
            j = i+L-1;
            m[i][j] = INT_MAX;
            for (k=i; k<=j-1; k++)
            {
                // q = cost/scalar multiplications
                q = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
                if (q < m[i][j])
                    m[i][j] = q;
            }
        }
    }
 
    return m[1][n-1];
}

* GeeksForGeeks * ( Too lazy to write again :P )



Tuesday, 26 January 2016

P vs NP : The million dollar question

Before getting to the million dollar question of this millennium (Seriously, Clay Mathematics Institute is offering $1 million to whoever solves it ), lets make ourselves familiar with the fancy terms P, NP, NP-Hard, NP-Complete.

First, let's understand what NP is.

Suppose you stumble upon a Sudoku puzzle that you just can't seem to solve. Then, a friend walks up to you and boasts, "Haha! I know the answer to the problem!" Amazed, you tell your friend, "Prove it!" Your friend fills out the empty spaces in your Sudoku puzzle, and behold, upon a quick glance, you've verified that the answer is correct. "How did you do that?" you asked your friend. He responds, "It doesn't matter ;)"

Sudoku is an NP problem because it you can "quickly" verify a solution to a Sudoku problem. (By "quickly", I really mean "in polynomial time.") It doesn't matter if you can actually figure out the solution quickly or not. All that matters for a problem to be in NP is if a solution can be verified quickly.


Now keep this little story in mind while reading these definitions :

P (Polynomial Time Running ) 


Consider linear search problem (searching a number in a list of numbers). You can find this number in linear time or O(n). Or consider binary search, that takes O(logn) time or a problem that can be solved in quadratic time O(n^2) time. 
All these problems have one thing in common, they can be "SOLVED" in polynomial time. 
if the algorithm runs in linear time or quadratic time or cubic time, then we say the algorithm runs in polynomial time and the problem it solves is in class P.



NP ( Non deterministic Polynomial Time) 


So in the story above, SOLVEing a problem may not have taken polynomial time, who knows what algorithm he used to solve the Sudoku. However, to "VERIFY" that he solved the Sudoku correctly, we  can do that in polynomial time! 
This means there is not necessarily a polynomial-time way to find a solution, but once you have a solution it only takes polynomial time to verify that it is correct. So if the solution to a problem can be checked in polynomial time, it is called NP problem.

NP is called "Non deterministic" because of Turning Machine. So this guy, Turing, invented a machine that can solve decision problems. In other words, if you feed the machine the problem and the solution, it can tell in polynomial time if the solution is correct or not (YES/NO). 

P.S. : 
I used to confuse it with Non Polynomial Time ( which would mean it can't be solved in polynomial time), a big blunder!


NP-HARD 


These are the rouge problems, like James Bond who dont like to play by the rules. NP-Hard problems need not be NP i.e. some problems that cant be even verified in polynomial time, they may or may not take PTIME to verify the solution. Unlike P and NP that are decision problems, NP Hard can be Decision, Search or Optimisation problems. These are called "Hard" because they are almost as hard as NP Complete problems. ( discussed next ).


NP Complete


-> NP
-> NP-Hard

As we know NP-Hard may or may not take PTIME to verify, those NP-Hard who takes PTIME are called NP-Complete. 
It has been proven that any NP-Complete problem can be converted to any other in PTIME using many-one reduction techniques!!! This leads to a very interesting theory...
  ___________________________________________
| Problem Type | Verifiable in P time | Solvable in P time |
____________________________________________              
| P                    |             Yes             |        Yes                |                                            
| NP                  |             Yes             |     Yes or No *       |                                            
| NP-Complete |              Yes             |      Unknown         |                                            
| NP-Hard         |           Yes or No **  |      Unknown ***     |                                            
____________________________________________


 

" ONE PROBLEM TO RULE THEM ALL ! "

 



Solve one, just one, any one! problem in PTIME, and rest can be solved automatically in PTIME. Sounds like magic, doesn't it? :D

Science behind the magic : Since any NP-complete problem can be reduced to any other NP-complete problem in polynomial time, all NP-complete problems can be reduced to any NP-hard problem in polynomial time. Then, if there is a solution to one NP-hard problem in polynomial time, there is a solution to all NP problems in polynomial time.

This leads to the ultimate debate, if NP problems could be solved in PTIME, is it true that...
P = NP ?

Its clear that P is a subset of NP. The think that isn't clear is if NP can be solve d in PTIME. cause if they can be, P would indeed be equal to NP.


Examples :


Travelling Salesman Problem (TSP) is a very famous real world problems that questions simply, If there are n cities connected to each other, let there be a salesman in city 1. Given the time between each pair of cities, Whats the minimum time such that he visits each city exactly once returns back to 1?

No efficient solution to this, but approximate optimal solutions are there.

TSP is a NP-complete problem because of two properties : 

1. No efficient solution in PTIME exists, but can be verified in PTIME.
2. Every instance of hamiltonian path is reducible to a TSP problem and it has been proven that Hamiltonian path problem is NP complete, TSP is NP complete.


References :


1. Stackoverflow
2. Quora