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


For those confused by the
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
@ 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'
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
what is exactly meant by convex segment of the points?.
@ stephen ...Even after
@ 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
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
@Stephen Merriman
could you tell me how is this condition valid
in the following code snippet
#define FORD(a,b,c) for(int a=b;a>=c;a--)
AND in my code instead of doing this
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
@all
CODING Lords.. please reply... am WAITING...
Using scanf is perfectly
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
^ 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
could u provide test cases for adding fraction problem..??
my code is quite simple but
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
@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
141/22/11/12/1@antar thanx alot dear......
@antar
thanx alot dear...... could u tell me how to generate test cases.........
@antar sir, the output for
@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
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
@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
+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
+1 there should be more clear specification of the problems. by this way site work as learning platform.
To count the number of ways,
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
+1 @arjun yes i too didn't understand what this line says?