Showing posts with label dp. Show all posts
Showing posts with label dp. Show all posts

Saturday, June 27, 2015

Dynamic Programming (DP) problems from SPOJ


Sphere Online Judge a.k.a. SPOJ is a very good online judge. Still, beginners face a lot of trouble when they first come to SPOJ, mainly because SPOJ is not as well categorized as some other judges out there. Recently SPOJ is trying to offer problem hints, but due to being community driven, this is still a long shot. Classification hints for SPOJ problems are not commonly available in the internet as well. So far I have managed to solve a few dynamic problems from SPOJ and I think for someone who is trying to practice dynamic programming problems from SPOJ, this short list might come handy.

ACMAKEREDISTMBLASTRAINBOW
ACODEEQ2MCARDSRENT
ACPC10DFISHERMDOLLSROCK
ACPC10GFPOLICEMISERMANSAMER08C
AIBOHPGCPC11BMIXTURESSCUBADIV
ANARC05BGNY07HMMAXPERSOLDIER
ANARC05HGNYR09FMPILOTSQRBR
ARBITRAGHANGOVERMREPLBRCSTONE2
BABTWRHELPBOBNOCHANGETEMPTISL
BYTESM2HIST2NUMPLAYTOURIST
CHAIRIKEYBNY10ETRAVERSE
COINSINGREDOSPROB1TRIKA
COUNTIOIPALINPARTITTRIP
CPRMTJRIDEPARTYTRT
CRSCNTRYKNAPSACKPCPC12GTWENDS
CSUBSEQSLINEUPPEGSUPSUB
CZ_PROB1M3TILEPERMUT1VOCV
DCEPC501MAIN112PHIDIASWPC4F
DINGRPMAIN113PIGBANKZUMA
DSUBSEQMARTIANPIZZALOC 
DTTMAXSUMSQPSTRING 

Please feel free to add more in the comments. Thanks


Saturday, March 19, 2011

DP @ UVa


300 DP problems from UVa, enjoy!

103 108 111 116 147 164 166 182 231 242
250 323 357 431 437 473 481 495 497 507
526 531 559 562 580 585 590 607 647 662
672 674 682 702 709 714 751 757 825 828
836 861 882 899 900 907 909 910 926 950
957 959 963 970 976 983 986 988 990 991
10003 10029 10032 10050 10051 10066 10069 10072 10074 10081
10086 10091 10097 10100 10118 10128 10130 10131 10149 10154
10157 10163 10192 10198 10201 10207 10239 10243 10247 10254
10261 10271 10280 10304 10306 10313 10324 10328 10337 10359
10364 10365 10405 10419 10444 10446 10453 10454 10465 10487
10497 10502 10516 10518 10529 10532 10534 10536 10541 10559
10564 10568 10590 10593 10597 10599 10604 10616 10617 10625
10626 10634 10635 10643 10648 10651 10654 10658 10667 10681
10684 10688 10690 10692 10694 10702 10711 10712 10715 10721
10722 10723 10733 10739 10743 10747 10755 10759 10788 10791
10811 10817 10819 10820 10826 10827 10835 10839 10859 10860
10874 10888 10891 10904 10908 10910 10911 10913 10918 10930
10934 10943 10953 10980 11002 11003 11008 11022 11026 11031
11040 11052 11067 11069 11077 11081 11084 11088 11091 11104
11125 11126 11133 11137 11151 11162 11169 11171 11176 11181
11191 11218 11229 11252 11258 11259 11261 11264 11266 11280
11282 11284 11285 11288 11293 11303 11307 11310 11311 11318
11320 11324 11328 11341 11361 11365 11370 11372 11375 11391
11394 11400 11404 11405 11413 11420 11421 11427 11431 11432
11433 11441 11444 11450 11456 11471 11472 11481 11485 11486
11499 11502 11514 11515 11517 11523 11531 11532 11534 11545
11546 11552 11553 11555 11566 11569 11578 11584 11590 11598
11601 11605 11611 11617 11645 11653 11691 11753 11790 11908
Courtesy: Jane Alam Jan & Felix Halim

Wednesday, August 4, 2010

Binary Tree -- N Nodes & H Height



The problem:


You are told that you have N unique numbers, how many different binary trees are possible with those N numbers (i.e. nodes) which has exactly H height.

Analysis:


If we look closely, it will be not that hard to figure out that, the problem actually wants to know, how many different binary trees are possible to build with N nodes, each of which has exactly H height allowing rotations. Here, two trees are different when their structures are different. For example:

@ @
/ \ / \
@ @ and @ @
/ \ / \
@ @ @ @


Solution:


I don't know whether this problem has a mathematical solution or not, I have searched for it and found nothing... But, if the values N and H supports, this problem can be solved with a dynamic programming approach.

When we want to build a tree with N nodes and H height, we can select a node as a root, so, we can put 1 to N-2 nodes on the left subtree of height H-1 or less and N-2 to 1 nodes on the right subtree of height H-1 or less, satisfying that, at least one of the subtree has a height H-1. So, by using proper base condition, we can find the number of different trees of exactly H height from this recursive formulation, and we can express the relation as follows,

if k == 1 then exactTrees( N, D ) = ( n == 1 )
if n <= 1 then exactTrees( N, D ) = 0
for all {L, R} such that L, R > 0 and L + R = N - 1:
exactTrees( N, D ) += exactTrees( L, D - 1 ) * exactTrees( R, D - 1 )
exactTrees( N, D ) += smallerTrees( L, D - 2 ) * exactTrees( R, D - 1 )
exactTrees( N, D ) += exactTrees( L, D - 1 ) * smallerTrees( R, D - 2 )

This is pretty obvious, because, if we want to get a tree of height H from any node, either one or both of its subtrees must have a height H - 1, and if we have N nodes at any step, we need to take 1 node as root, and then if we take i nodes for left subtree, we have N - 1 - i nodes left for the right subtree.
When we calculate exact height, we need to consider that a subtree may have a smaller height, so we need to calculate smallerTrees( N, D ) separately, but actually it is a bit easier:

if n < 0 or k <= 0 then smallerTrees( N, D ) = 0
if k == 1 then smallerTrees( N, D ) = ( n == 1 )
if n <= 1 then smallerTrees( N, D ) = 1
else
for all {L, R} such that L, R > 0 and L + R = N - 1:
smallerTrees( N, D ) += smallerTrees( L, D - 1 ) * smallerTrees( R, D - 1 )


Sample program:


First one goes the recursive algorithm (with dp):

// states: nodes, height
int exact[202][101], smaller[202][101];

int smallTrees(int n, int k)
{
if(n<0 || k<=0) return 0;
if(k==1) return n==1;
if(n<=1) return 1;

int &ret = smaller[n][k], l, r;
if(ret!=-1) return ret;

ret = 0;
for(int i=1; i<n-1; i++)
{
l = i, r = n-1-i;
if(l && r)
{
ret += smallTrees(l, k-1)*smallTrees(r, k-1);
}
}
return ret;
}

int exactTrees(int n, int k)
{
if(k==1) return n==1;
if(n<=1) return 0;

int &ret = exact[n][k], l, r;
if(ret!=-1) return ret;
ret = 0;
for(int i=1; i<n-1; i++)
{
l = i, r = n-1-i;
if(l && r)
{
ret += exactTrees(l, k-1)*exactTrees(r, k-1);
ret += exactTrees(l, k-1)*smallTrees(r, k-2);
ret += smallTrees(l, k-2)*exactTrees(r, k-1);
}
}
return ret;
}

Here is the iterative version (a little complex):

// states: height, nodes
int exactTrees[101][202], smallTrees[101][202];

void solve(int n, int k)
{
int d, i, j;
exactTrees[1][1] = 1;
for(d=2; d<=k; d++) // depth
{
for(i=1; i<=n; i+=2) // number of nodes
{
for(j=1; j<=i-1; j+=2) // left & right
{
exactTrees[d][i] += exactTrees[d-1][j]*exactTrees[d-1][i-1-j];
exactTrees[d][i] += exactTrees[d-1][j]*smallTrees[d-2][i-1-j];
exactTrees[d][i] += smallTrees[d-2][j]*exactTrees[d-1][i-1-j];
}
}
for(i=1; i<=n; i+=2) // update smaller tree
{
smallTrees[d-1][i] += smallTrees[d-2][i]+exactTrees[d-1][i];
}
}
}

[P.S. The result of this problem grows very rapidly along with the increase of N and H, so, it may easily run out of range, normally problems like this requires the result with respect to some modulus values.]

Similar problem:


USACO Section: 2.3, Problem: Cow Pedigrees, Code: nocows.
[P.S. Links won't work for usaco, you need to reach level 2.3 if you want to see the problem yourself.]
If you find any more problem regarding this, please post it as a comment below.

Sunday, August 23, 2009

Calculate nCr using dp


Calculate nCr with out having overflow when it is guaranteed that the final result will not overflow:



From pascal's triangular relation, we get,
nCr = n-1Cr + n-1Cr-1

Using this recursive formula directly will lead the program to exceed the time limit, as this may calculate the same value for many times which is un-necessary and we can remove this part by saving the states which means by using dynamic programming concepts.

In this formulation, one thing is to be noted that n and r keep decreasing, and sometimes is is possible that n becomes smaller than r. So considering these cases we get our base conditions for the recursive formula.


We know,
nCn = 1
nC1 = n
nC0 = 1
and
nCr = n-1Cr + n-1Cr-1

So, we can build the recursive function as follows:

function nCr(n, r):
if n == r:
return 1
if r == 1:
return n
if r == 0:
return 1
return nCr(n-1, r) + nCr(n-1, r-1)

Now, to reduce recursive steps, we maintain a table for saving the values of nCr of intermediate steps. So, when we face a sub-problem which is already solved, we can look up its value from the pre-calculation table.

table dp[N][R]

function nCr(n, r):
if n == r:
dp[n][r] = 1
if r == 1:
dp[n][r] = n
if r == 0:
dp[n][r] = 1
if dp[n][r] is not yet calculated:
dp[n][r] = nCr(n-1,r) + nCr(n-1,r-1)
return dp[n][r]


Here is a sample code written in C++ which demonstrates the idea. (It is assumed that MAX N is 65 and N >= R).


#include <stdio.h>

#define i64 unsigned long long

i64 dp[66][33];

i64 nCr(int n, int r)
{
if(n==r) return dp[n][r] = 1;
if(r==0) return dp[n][r] = 1;
if(r==1) return dp[n][r] = (i64)n;
if(dp[n][r]) return dp[n][r];
return dp[n][r] = nCr(n-1,r) + nCr(n-1,r-1);
}

int main()
{
int n, r;
while(scanf("%d %d",&n,&r)==2)
{
r = (r<n-r)? r : n-r;
printf("%llu\n",nCr(n,r));
}
return 0;
}

Plain and simple!!!