CodeChef is a non-commercial competitive programming community
Login
Username (New User? Signup) Password (Forgot Password?)
Signup
Login or
Signup with
Connect
Note
  • Publicize your achievements on your Facebook Wall.
  • Challenge your friends or ask them for help.

Site Navigation

  • PRACTICE
    • Easy
    • Medium
    • Hard
    • Challenge
    • Peer
  • COMPETE
    • All Contests
    • June Long 2012
    • May Cook-Off
    • May Long 2012
  • DISCUSS
    • Forums
    • Blog
    • Wiki
    • Facebook
    • Twitter
  • COMMUNITY
    • CodeChef Meetups
    • Campus Chapters
    • Host your Contest
    • User Groups
    • CodeChef TechTalks
    • All Educational Initiatives
  • HELP
    • Frequently Asked Questions
    • FAQ for problem setters
    • Problem Setting
    • Tutorials
    • Long Contest Ranks
    • Short Contest Ranks
    • Event Calendar
  • ABOUT
    • About CodeChef
    • Team CodeChef
    • Press Room
    • CodeChef Financials
    • CodeChef Sponsorships
    • CEO's Corner
    • Contact Us
    • About Directi
Home » Wiki » April 2010 Contest Problem Editorials

April 2010 Contest Problem Editorials

1) Travelling Salesman Again ! (written by Varun Jalan aka Syco)

If a trip from i to j is taxed, so is a trip from i-1 to j+1, i - 1 to j, and i to j + 1. We can use this to show that g[i][j] + g[i+1][j+1] <= g[i+1][j] + g[i][j+1]. Thus, the cost matrix is Monge.

A monge array can be shown to satisfy the Demidenko conditions. These conditions are necessary and sufficient conditions that a cost matrix should satisfy in order that the optimal tour be bitonic (first
increasing and then decreasing) For example an optimal tour can be something like : 1 3 4 5 9 8 7 6 2 1.

This allows a DP solution, with state (i,j) which is the value of the optimal tour passing through all nodes between indices i to j. (Treat the optimal tour as two paths, one from 0 to n - 1, and the other from
n - 1 to 0, having no vertices except 0 and n - 1 in common). Time complexity : O(n^2 + K)

There is also an O(n) solution to the problem too, once the g matrix is known. Each entry of the g matrix can be computed in O(log ^2 K) time using a data structure such as a segment tree. This leads to an
O(n log ^2 K + K) solution.

2) Adding Fractions ((written by Varun Jalan aka Syco)

Consider plotting the points (yi,xi) on the plane. For each point i we add, we wish to know what is the point j, along with which the point i makes the maximum slope line when the two points are joined. Note that this point lies on the convex segment of the points p0..p(i-1). Thus we keep a convex segment of the points on a stack as we proceed. When we encounter the point i, we keep popping points from the stack till the current point does not form a concave segment with the last two
points on the stack. The last point on the stack is the optimal j we were looking for. Push i on the stack, and proceed. Since each point is pushed/popped at most once, we have a linear time algorithm.

3) Trip (written by Varun Jalan aka Syco)

It is easy to see that in order to make the least number of stops, one should cover as much distance as possible each time. We can compute this in linear time, by maintaining two pointers p1 and p2 such that a[p2] - a[p1] <= M, but a[p2+1] - a[p1] > M. We can decrement p1 and adjust p2 accordingly.

Thus we get get for each point i, dp[i], which is the minimum number of steps to reach the destination from location i. To count the number of ways,  we have dp[i] = sum dp[j], such that dp[j] = dp[i+1], and a[j] - a[i] <= M. We can easily compute the dp[] array in linear time as well.

4) Lighting (written by Duc)

Construct a bipartite graph in which one set of vertices corresponds to the rows and the other corresponds to the columns of the board. There is an edge from i to j if a[i,j] = 1. The problem asks to find the minimum edge coloring of the graph, i.e. to find the minimum number of colors to color the edges such that none of adjacent edges have the same color.

Minimum edge coloring in general graph is shown to be NP-hard, although an interesting result (Vizing's theorem) says that the minimum number of colors needed (or edge chromatic number) is either D or D+1, for D is the maximum degree of the graph.

However, on bipartite graph, minimum edge coloring is shown to be in P. A theorem by Konig says that in bipartite graph, edge chromatic number is equal to maximum degree. The theorem also suggests an O(VE) algorithm to find the minimum edge coloring.

A proof about Konig's theorem can be found here http://www.kurims.kyoto-u.ac.jp/~iwata/dmi/dmi01e.pdf. There is O(ElogE) time algorithm has been developed, for example http://ecommons.cornell.edu/handle/1813/6283. Some contestants manage to optimize the O(VE) to run into the time limit.

5) Reversed (written by Zac)

There is a linear-time solution to the problem.  The details can get quite technical, so I'll just explain the high-level ideas.  As a warm-up, consider the following problem.  Say we just want to find the least integer C' using the digits of C such that C' > A and C' > B (don't worry about the reversed part yet).  Assume A >= B so we just need to worry about C' > A.  The basic idea is the following.  For any such arrangement C' of C such that C' > A, we can break C' into three parts.  First is the common prefix of C' and A.  Next is the single digit following this prefix which must be greater than the corresponding digit of A.  Finally, we have the remaining digits.  A key observation is that if C'_1 and C'_2 are both greater than A and C'_1 < C'_2, then then length of the common prefix of C'_1 and A is at least the length of the common prefix of C'_2 and A.

A linear-time algorithm for solving this simpler problem is as follows.  Create the longest possible string out of the digits of C that is a prefix of A.  Call this prefix P and say it's length is |P|.  Now, if all remaining digits of C are less than the (|P|+1)st digit (from the left) of A, then iteratively delete the last digit of P until there is a digit of C not used in P that exceeds the (|P|+1)st digit of A.  Once we have found such a digit, append it to P.  Finally, now that P exceeds the length |P| prefix of A, we are free to assign the remaining digits in any way.  The least such way is to first append the remaining 0s to P, then the remaining 1s, and so on.

For the more general problem, finding the longest common prefix of a solution is still a good guiding principle.  For a string S such that S > (both length |S| prefixes of A and B), we can append the remaining n-|S| digits of C in any order to have the resulting string exceed A and B.  Let S' be obtained by appending the remaining 9s to S, then the remaining 8s, and so on.  Then there exists a C' that extends S with C' > A, C' > B, C'^R < A^R, and C'^R < B^R if and only if this string S' is such a solution.  An algorithm proceeds as in the previous paragraph, except we iteratively delete digits of the prefix P until both the condition in the preceding paragraph is satisfied and appending the remaining digits to P in decreasing order results in a valid solution.  This is possible to do in linear time (i.e. constant time per deletion of the last digit of P) with the right bookkeeping.

Now, we are left with a string P such that P > (both length |S| prefixes of A and B) and such that there is a way to append the remaining digits to P to satisfy the conditions on the reversed numbers.  A similar algorithm can be employed to append these digits in such a way as to form the least possible C' that satisfies both the original and reversed inequalities.

6) Flood (written by Duc and Zac)


This problem can be seen as an instance of the Node Weighted Steiner Tree problem in a grid graph. It is proven that in general graphs, no approximation algorithm can guarantee an approximation ratio that is asymptotically better than log n. However, we do have c log n approximation algorithm for some fixed constant c, for example a 2 log n approximation algorithm exists. An even stronger result is available. That is, there exists some constance for which no c log n approximation algorithm exists, unless P=NP [1]. The same result probably also holds in a grid graph too. However, there may also be better approximation algorithms for grid graphs.

Nonetheless, the 2 logn approximation algorithm is not time efficient enough for our problem. So we need to resolve to other heuristics. Here we present an approximation algorithm by Zac:
- Let all the cells be nodes in a graph
- For each cell/node, add edges to the surrounding (up to) eight cells/nodes
- For each edge, set the weight of this edge to be the sum of the weights of the endpoint cells (an island has weight 0)
- From now on, ignore the node weights and consider only the edge weights
- Find a minimum spanning tree of this graph
- Iteratively prune all leaves until only islands are leaves
- Each non-island node remaining in this tree is to be used in the final solution

Of course, there are other tricks (e.g. local search improvements) that one can employ to try to improve this solution. The solution is valid since the sub-tree we find is connected and spans all islands.  The weights of the edges are supposed to somehow capture the cost of using the endpoints in the final solution. Of course, this is not a great approximation as there are some bad examples exist.  However, it did work fairly well on the randomly generated instances.

References
[1] Philip N. Klein, R. Ravi: A Nearly Best-Possible Approximation Algorithm for Node-Weighted Steiner Trees. J. Algorithms 19(1): 104-115 (1995)

If you would like to see how the final verion of the editorial for 'FLOOD' was created check out the following conversation at Zac and Duc's conversation on Flood




Comments

  • Login or Register to post a comment.

For those confused by the

triplem @ 14 Apr 2010 09:10 AM

For those confused by the terminology of 'Monge' and 'Demidenko conditions', it can be explained without knowledge of those terms.

Due to the g[i][j] + g[i+1][j+1] <= g[i+1][j] + g[i][j+1] condition mentioned, you never need to go 'up down up' or 'down up down' - for example, 1 to 3 to 2 to 4 will never be part of the solution, as 1 to 2 to 3 to 4 is shorter. Thus you only need to consider sequences which increase then decrease, as mentioned.

@ Stephen : I agree that the

syco @ 14 Apr 2010 10:01 AM

@ Stephen : I agree that the obsevation you mention provides very good intuition to solve the problem, however it is not enough to prove the optimality of bitonic tours. For example, a tour : 1 4 7 10 9 6 3 2 5 8 0 (k-tonic in general) does not have an "up down up" or "down up down" contiguous sequence, however we need not consider such tours. Thus the two conditions are not sufficient and more are needed. Those are provided too by the Demidenko contions. Rightly so, 2 of the 4 Demidenko conditions are the ones that you mention.

Yeah, I was meaning 'down'

triplem @ 14 Apr 2010 11:14 AM

Yeah, I was meaning 'down' and 'up' in terms of decreasing + increasing sequences rather than single elements. Definitely still several details needed in a proof :P

what is exactly meant by

gautamverma @ 14 Apr 2010 11:18 AM

what is exactly meant by convex segment of the points?.

@ stephen ...Even after

mcsharma1990 @ 14 Apr 2010 01:29 PM

@ stephen ...Even after knowing that the sequence will be first increasing then decreasing there are still 2^(n-1) possible no. of tour (if i may correct). then how to find the optimal path?

I know that this is explained above but i am not getting it. Can you please explain? Thankyou in advance :)

I used the DP method

triplem @ 14 Apr 2010 02:01 PM

I used the DP method mentioned.

Consider the cities from 0 to N-1 in order. Let f[x][y] = the minimum distance given that x was the last city we visited on the way up, and y was the last city that we visited on the way down.

Then the next city z = max(x+1,y+1).

If z is the last city (ie = N-1), we store the distance from x to z and the distance from z back to y, ie f[x][y] = dist[x][z] + dist[z][y].

Otherwise, we can either visit this city on the way up (f[x][y] = dist[x][z] + f[z][y]) or skip it and visit it on the way back (f[x][y] = dist[z][y] + f[x][z]). Choose the minimum option.

The answer is then f[0][0].

@Stephen Merriman could you

vermapratyush @ 14 Apr 2010 06:53 PM

@Stephen Merriman

could you tell me how is this condition valid

  • if( ans[i][0]*ans[x][1] <= ans[x][0]*ans[i][1] )
  •  

     

    in the following code snippet

    #define FORD(a,b,c) for(int a=b;a>=c;a--)

  • FORD(i, n-2, 0 )
  • {
  • x = i+1;
  • ans[i][0] = A[i][0]; ans[i][1] = A[i][1];
  • do
  • {
  • if( ans[i][0]*ans[x][1] <= ans[x][0]*ans[i][1] )
  • {
  • ans[i][0] += ans[x][0];
  • ans[i][1] += ans[x][1];
  • x = till[x] + 1;
  • }
  • else break;
  • }while( x < n);
  • till[i] = x - 1;
  • }
  • AND in my code instead of doing this

    				if( ans[i][0]*ans[x][1] <= ans[x][0]*ans[i][1] )

     

    i did this ...

    *                if(ip_N[i]/ip_D[i] > ((ip_N[i]+ans_N[i+1])/(ip_D[i]+ans_D[i+1])) )

    and i got TLE... y??? there is difference of just one comparison rest of the code is quite similar ...

     

     

    I checked submissions of many ppl and some had algos similar to mine but with different INPUT/OUTPUT strategy.. could you help me in that field..

    this was my i/p taking method

     

    scanf("%ld%c%ld",&ip_N[i],&ch,&ip_D[i]);

     

    is it not the proper way of taking Input.. in CODECHEF..??

    @all CODING Lords.. please

    vermapratyush @ 15 Apr 2010 07:30 PM

    @all

    CODING Lords.. please reply... am WAITING...

    Using scanf is perfectly

    abhijith @ 15 Apr 2010 08:57 PM

    Using scanf is perfectly normal, but for a few problems here, input is quite large and hence even scanf is slow in those cases and hence people resort to alternate ways of reading input (fread,read etc..)

    ^ yeah , using getchar() in a

    f03nix @ 15 Apr 2010 11:24 PM

    ^ yeah , using getchar() in a loop too turns out to be faster than scanf.

    And its not just for input, sometimes inefficient output too can cause an otherwise acceptable solution to time out. In the reversed problem, printing the digits in a loop as integers when compared to printing it as a string adds about a complete second to the time.

    could u provide test cases

    dabbcomputers @ 16 Apr 2010 08:01 PM

    could u provide test cases for adding fraction problem..??

    my code is quite simple but

    dabbcomputers @ 16 Apr 2010 08:02 PM

    my code is quite simple but getting WA

     

    #include<stdio.h>

     

    long int  gcd(long int a, long int b)

    {

     

    if (b==0)

    return a;

    else

    return (long int)gcd(b,a%b);

     

    }

    int main()

    {

    long  t,i,j=0,n,temp,ansa,ansb,ans;

    double *a,*b,*stacka,*stackb;

    char ch[500];

    scanf("%ld",&t);

    while(t--)

    {

    scanf("%ld",&n);

    a=(double *) malloc(n*sizeof(double));

    b=(double *) malloc(n*sizeof(double));

    stacka=(double *) malloc(n*sizeof(double));

    stackb=(double *) malloc(n*sizeof(double));

    for(i=0;i<n;i++)

    {

    temp=0;

    scanf("%s",ch);

    j=0;

    while(ch[j]!='')

    {

    if(ch[j]!='/')

    temp=temp*10+(ch[j]-'0');

    else

    {

    a[i]=temp;

    temp=0;

    }

    j++;

    }

    b[i]=temp;

    temp=0;

    }

    for(i=n-1;i>=0;i--)

    {

    stacka[i]=a[i];

    stackb[i]=b[i];

    j=i+1;

    while((float)(stacka[i]/stackb[i])<(float)((a[j])/(b[j]))&&j<n)

    {

    stacka[i]+=a[j];

    stackb[i]+=b[j];

    j++;

    }

    }

    for(i=0;i<n;i++)

    {

    ansa=stacka[i];

    ansb=stackb[i];

    ans=gcd(ansa,ansb);

    stacka[i]=ansa/ans;

    stackb[i]=ansb/ans;

    printf("%ld/%ldn",ansa/ans,ansb/ans);

    }

    printf("n");

    }

    return 0;

    }

    @amrit Mate , you really

    f03nix @ 16 Apr 2010 09:36 PM

    @amrit

    Mate , you really shouldn't post your code here in the comments . Imagine if everyone who had a problem did so .... it'd really makes the whole place a mess.

    Now since the problem's solution are available, my recommendation would be that you generate (random) your own test cases and test your program against other accepted solutions. You'll know whats wrong in your program.

    for a specific test case where your program can fail, try this one

    1
    4
    1/2
    2/1
    1/1
    2/1

    @antar thanx alot dear......

    dabbcomputers @ 17 Apr 2010 07:51 PM

    @antar

    thanx alot dear...... could u tell me how to generate test cases.........

    @antar sir, the output for

    dabbcomputers @ 17 Apr 2010 08:17 PM

    @antar

    sir, the output for the successful solution the output is

    6/5

    2/1

    3/2

    2/1

    and my code show output

    5/4

    2/1

    3/2

    2/1

    but the according to the statement 5/4 is equal to 1.25 and 6/5 is equal to 1.2 so the answer should be 5/4.

    then how these solutions are got accepted. plsss chk it out......

    It is not possible to form

    triplem @ 18 Apr 2010 02:52 AM

    It is not possible to form the fraction 5/4 by adding a sequence of fractions starting from the first position.

    1/2 = 1/2

    1/2 '+' 2/1 = 3/3

    1/2 '+' 2/1 '+' 1/1 = 4/4

    1/1 '+' 2/1 '+' 1/1 '+' 2/1 = 6/5.

    @zac In your solution to

    Awake @ 22 Apr 2010 03:12 PM

    @zac

    In your solution to reversed::

    I think meeting the reverse number conditions was the good part of the algorithm and  you skipped all of it !

    so it would be of great help if you( or ne1) could explain how to arrange the remaining digits in order to satisfy the reverse numbers constraints?

    +1 . Its the same with all

    flying_ant @ 22 Apr 2010 07:34 PM

    +1 . Its the same with all the problems. Could have been written in a lot more detail, with references to the algorithms here and there.. with small code snippets, explaining the steps more neatly .., so that it will be useful for beginners a lot. After all you need to do this for only 6 problems a month. May be Paid editorials will make the difference :)

    +1 there should be more clear

    dabbcomputers @ 22 Apr 2010 11:02 PM

    +1 there should be more clear specification of the problems. by this way site work as learning platform.

    To count the number of ways, 

    illusionist @ 26 Apr 2010 04:47 PM

    To count the number of ways,  we have dp[i] = sum dp[j], such that dp[j] = dp[i+1], and a[j] - a[i] <= M. We can easily compute the dp[] array in linear time as well.

    Can anyone explain this concept a little better i didn't get it....

    +1 @arjun yes i too didn't

    codegambler @ 30 Apr 2010 10:43 AM

    +1 @arjun yes i too didn't understand what this line says?

    CodeChef is a non-commercial competitive programming community
    • About CodeChef
    • About Directi
    • CEO's Corner
    • C-Programming
    • Programming Languages
    • Contact Us
    © 2009 Directi Group. All Rights Reserved. CodeChef uses SPOJ © by Sphere Research Labs
    In order to report copyright violations of any kind, send in an email to copyright@codechef.com
    CodeChef a product of Directi
    The time now is:
    CodeChef - A Platform for Aspiring Programmers

    CodeChef was created as a platform to help programmers make it big in the world of algorithms, computer programming and programming contests. At CodeChef we work hard to revive the geek in you by hosting a programming contest at the start of the month and another smaller programming challenge in the middle of the month. We also aim to have training sessions and discussions related to algorithms, binary search, technicalities like array size and the likes. Apart from providing a platform for programming competitions, CodeChef also has various algorithm tutorials and forum discussions to help those who are new to the world of computer programming.

    Practice Section - A Place to hone your 'Computer Programming Skills'

    Try your hand at one of our many practice problems and submit your solution in a language of your choice. Our programming contest judge accepts solutions in over 35+ programming languages. Preparing for coding contests were never this much fun! Receive points, and move up through the CodeChef ranks. Use our practice section to better prepare yourself for the multiple programming challenges that take place through-out the month on CodeChef.

    Compete - Monthly Programming Contests and Cook-offs

    Here is where you can show off your computer programming skills. Take part in our 10 day long monthly coding contest and the shorter format Cook-off coding contest. Put yourself up for recognition and win great prizes. Our programming contests have prizes worth up to Rs.20,000 and $700lots more CodeChef goodies up for grabs.

    Discuss

    Are you new to computer programming? Do you need help with algorithms? Then be a part of CodeChef's Forums and interact with all our programmers - they love helping out other programmers and sharing their ideas. Have discussions around binary search, array size, branch-and-bound, Dijkstra's algorithm, Encryption algorithm and more by visiting the CodeChef Forums and Wiki section.

    CodeChef Community

    As part of our Educational initiative, we give institutes the opportunity to associate with CodeChef in the form of Campus Chapters. Hosting online programming competitions is not the only feature on CodeChef. You can also host a coding contest for your institute on CodeChef, organize an algorithm event and be a guest author on our blog.

    Go For Gold

    The Go for Gold Initiative was launched about a year after CodeChef was incepted, to help prepare Indian students for the ACM ICPC World Finals competition. In the run up to the ACM ICPC competition, the Go for Gold initiative uses CodeChef as a platform to train students for the ACM ICPC competition via multiple warm up contests. As an added incentive the Go for Gold initiative is also offering over Rs.8 lacs to the Indian team that beats the 29th position at the ACM ICPC world finals. Find out more about the Go for Gold and the ACM ICPC competition here.

    Domain Name Registration, Web hosting, and Website Design provided by BigRock.com