One Point Solution

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Tuesday, 6 December 2011

SRM 526: The killing wait for results

Posted on 20:01 by Unknown
While I wait for results, here is my perspective on this algorithm contest.

It began with issues, it had to be postponed 15 minutes. TC has been misbehaving in the technical side lately.

So, let us return to a simple contest with the usual strategy to try to solve the medium problem first.

Div1 500: PrimeCompositeGame


Let us begin with a dynamic programming solution that runs in O(N*K) time. It is kind of standard but is not fast enough. The good news is that it is possible to turn it into O(N*log(N)).

It is the usual game theory approach for dynamic programming. Let us think of a recurrence F(T, N), it will give the result (negative # of steps if first player loses, positive if he wins) in the sub-case where it is Player T's turn (for convenience the first player is player 0, and the second is 1). If there are N stones.

To evaluate a given state F(T, N), pick all the K possibilities of the stones to remove (You can remove from 1 to K stones). And find F( !T, N - removed_amount). Then player #T would pick the most convenient number of rocks to remove based on that solution. Note that some of the moves are not valid and if no valid moves exist, the player just lost.

We now need to turn it into O(N*log(N)). Just notice that when solving F(T, N), we need to consider the previous values F(! T, x) where x is between (N-K and N-1). Of them, we would need to find, depending on the player , the values of F(!T, x) that are negative or positive and also depending of the player, the maximum and minimum of those values. By using a std::set you can do this if you add the appropriate values to the appropriate set after each evaluation for a value of N. Just make sure to remove values of x that are smaller than N-K if you find them.

The following code is awful and could get plenty of fixes, but it shows the idea, I think.

bool iscomposite[474748]; 
int dp[2][474748];
struct PrimeCompositeGame
{
int theOutcome(int N, int K)
{
memset(iscomposite,0, sizeof(iscomposite));
for (int i=2; i<=N; i++) {
if (! iscomposite[i]) {
for (int j=i+i; j<=N; j+=i) {
iscomposite[j] = true;
}
}
}
dp[0][1] = -1;
dp[1][1] = 1;


set< pair<int,int> > primePositive[2];
set< pair<int,int> > primeNegative[2];
set< pair<int,int> > compoPositive[2];
set< pair<int,int> > compoNegative[2];

for (int n=2; n<=N; n++) {
for (int cplayer=0; cplayer<2; cplayer++) {
//player 0
if (cplayer==0) {
//look for a positive dp[!cplayer][x] in the range n-K <= x <= n-1
// if it exists, return the minimum.
bool found = false;
int pick = 0;
{
set< pair<int,int> > & tm = primePositive[!cplayer];
while(! tm.empty() ) {
set<pair<int,int> >::iterator q = tm.begin();
if ( q->second < n-K ) {
tm.erase(q);
} else {
found = true;
pick = q->second;
break;
}
}
}

// if it does not exist, return the minimum negative dp[!player][x]
if (! found ) {
set< pair<int,int> > & tm = primeNegative[!cplayer];
while(! tm.empty() ) {
set<pair<int,int> >::iterator q = tm.begin();
if ( q->second < n-K ) {
tm.erase(q);
} else {
found = true;
pick = q->second;
break;
}
}
}

if(found) {
if (dp[!cplayer][pick] < 0) {
dp[cplayer][n] = dp[!cplayer][pick] - 1;
} else {
dp[cplayer][n] = dp[!cplayer][pick] + 1;
}
} else {
dp[cplayer][n] = -1;
}
}
// player 1
if (cplayer==1) {
//look for a negative dp[!cplayer][x] in the range n-K <= x <= n-1
// if it exists, return the maximum
bool found = false;
int pick = 0;
{
set< pair<int,int> > & tm = compoNegative[!cplayer];
while(! tm.empty() ) {
set<pair<int,int> >::reverse_iterator q = tm.rbegin();
if ( q->second < n-K ) {
tm.erase( tm.find(*q) );
} else {
found = true;
pick = q->second;
break;
}
}
}

// if it does not exist, return the maximum positive dp[!player][x]
if (! found ) {
set< pair<int,int> > & tm = compoPositive[!cplayer];
while(! tm.empty() ) {
set<pair<int,int> >::reverse_iterator q = tm.rbegin();
if ( q->second < n-K ) {
tm.erase( tm.find(*q) );
} else {
found = true;
pick = q->second;
break;
}
}
}

if(found) {
if (dp[!cplayer][pick] < 0) {
dp[cplayer][n] = dp[!cplayer][pick] - 1;
} else {
dp[cplayer][n] = dp[!cplayer][pick] + 1;
}
} else {
dp[cplayer][n] = 1;
}

}
}
// add to the sets
for (int cplayer=0; cplayer<2; cplayer++) {
assert(dp[cplayer][n] != 0);
if( iscomposite[n] ) {
if (dp[cplayer][n] > 0) {
compoPositive[cplayer].insert( make_pair(dp[cplayer][n], n) );
} else {
compoNegative[cplayer].insert( make_pair(dp[cplayer][n], n) );
}
} else {
if (dp[cplayer][n] > 0) {
primePositive[cplayer].insert( make_pair(dp[cplayer][n], n) );
} else {
primeNegative[cplayer].insert( make_pair(dp[cplayer][n], n) );
}
}
}
}


cout<<dp[0][N]<<endl;

int y = dp[0][N];
if (y > 0) {
return y - 1;
} else {
return y + 1;
}
}
};


During the match I had many problems implementing this. Which should be clear by the size of the code and the amount of repetition in it. I had to debug many problems and change the code a lot during the match.

DuckAligment


I will admit that by the time I opened this problem, I was confident about my 500. Its algorithm is correct after all, and its main risk was memory/time limits and I tested the largest case.

I checked the division summary, and it seemed anything above 230 was going to be a great score and seemed doable. So, I actually scored 230 points...

In problems like this, it is best to verify that the target solution space is rather small. There are at most O(N) columns/rows. And in each column/row, the first point of the line of ducks can be one of O(N) values. This means that the final setup of ducks can be one of O(N*N), we can just iterate through them all.

Once the final position is fixed, it will either be a line of ducks in a row or in a column. We can treat each case separately and they are in fact, symmetric (After solving for rows, just rotate your logic to solve for columns). So, given a row, and the cell in that row in which the line of ducks will begin - What is the minimum cost to move the ducks to those positions?

Now, let us think about the ducks themselves. Note that since the row in each of the target cells is always the same, the vertical cost will always be the same for a duck at a row y, no matter what cell we choose it to go. (The cost is y - i, where i is the number of the picked row). So we should only care about minimizing the horizontal distances. This is solvable with a greedy approach:

Let us say we have a row of ducks:
..*.*...**..*.*.*.
(This is the row after we move each duck to a position in the row, without accounting for horizontal moves).

And we want to turn it into:
....*******.......
(The target row).

Then you have to notice that the optimal way to move the left-most duck is to the left-most target cell. The second duck to the left, should move to the second target cell to the left. And so and so. We can simply sort the ducks by horizontal coordinate and move the i-th duck in the sorted list to the i-th cell in the target setup.

For columns , do exactly the same, just sort the ducks by row instead of column.

    int minimumTime(vector <string> grid) 
{
int w = grid.size();
int h = grid[0].size();
int n = 0;

vector< pair<int,int> > ducks;

for (int i=0; i<w; i++) {
for (int j=0; j<h; j++) {
if (grid[i][j] == 'o' ) {
ducks.push_back( make_pair(j,i) );
}
}
}
n = ducks.size();
sort(ducks.begin(), ducks.end() );

int res = 1000000000;

// a row
for (int x=0; x<w; x++) {
for (int y=0; y+n <= h; y ++) {
int cost = 0;
for (int i=0; i<n; i++) {
cost += abs( ducks[i].second - x) + abs( ducks[i].first - (y+i) );
}
res = std::min(res, cost);
}
}

// a column
for (int i=0; i<n; i++) {
swap( ducks[i].first, ducks[i].second );
}
sort(ducks.begin(), ducks.end() );
for (int y=0; y<h; y++) {
for (int x=0; x+n <= w; x ++) {
int cost = 0;
for (int i=0; i<n; i++) {
cost += abs( ducks[i].second - y) + abs( ducks[i].first - (x+i) );
}
res = std::min(res, cost);
}
}

return res;

}


Outcome


I passed system tests on both problems and got a very good position.

The admins are considering whether to make the match rated or not. I don't think it makes any sense to cancel the ratings in this match. The expected rating change for a coder that participates in a match is 0. Therefore, coders that were unable to participate did not lose (or gain) any rating unfairly. It is lame that they had to lose the match, and I hope the admins make a new December SRM. But the objective of the ratings is to measure performance objectively, and the ratings of the match do exactly that , because they consider only the coders that participated.
Email ThisBlogThis!Share to XShare to Facebook
Posted in explanation, srm, topcoder | No comments
Newer Post Older Post Home

0 comments:

Post a Comment

Subscribe to: Post Comments (Atom)

Popular Posts

  • TopCoder SRM 557 - finally
    SRM 557 Explanation for division 1 Easy and match recap. Explanations for div2 easy and div2 medium. It feels like it has been ages since t...
  • SRM 589 Editorial
    I have finished writing the editorial for TopCoder SRM 589: http://apps.topcoder.com/wiki/display/tc/SRM+589 . As you most likely noticed. L...
  • SRM 590 recap and editorial
    Another week another Topcoder match. Not a great day. I had a bad flu and still do. Div1 500: The one with Xor Given a list of cards with nu...
  • SRM 546: relief
    I figured I should post something about this SRM. I've been very busy these weeks because the semester is ending and I tried to win a t-...
  • SRM 601 editorial (minus div1 hard)
    It is up: http://apps.topcoder.com/wiki/display/tc/SRM+601 This was a very dry editorial to write. All problems were mathy ad hoc or complex...
  • Member SRM 505: Part 1
    So, let me explain a couple of problems from a Topcoder Member SRM that I wrote and never got an editorial. BTW, it was the last member SRM....
  • SRM 533: Div1 500 MagicBoard explanation
    Finally solved it. It is a nice problem that is worth explaining in a post. You have a grid/board of at most 50x50 cells. Some cells contain...
  • SRM 554 div1 hard: TheBrickTowerHardDivOne
    Link to problem statement We got infinitely many bricks of dimensions 1x1x1 and C different colors. Count the number of towers of size 2x2...
  • SRM 526: The killing wait for results
    While I wait for results, here is my perspective on this algorithm contest. It began with issues, it had to be postponed 15 minutes. TC has ...
  • TopCoder SRM 570: CentaurCompany and CentaurCompanyDiv2
    Another 570 editorial update: http://apps.topcoder.com/wiki/display/tc/SRM+570 . This time for the division 2 hard and division 1 medium. My...

Categories

  • acm
  • algorithm
  • answers
  • arenaplugin
  • badday
  • behindthescenes
  • bugs
  • c++
  • censorship
  • codechef
  • codeforces
  • contests
  • crocchamp
  • editorial
  • editorial.srm
  • embarrassing
  • explanation
  • gcj2013
  • gmp
  • goodday
  • google
  • googlecodejam
  • greed
  • groklaw
  • health
  • html
  • httpseverywhere
  • implementation
  • ipsc
  • ispc
  • java
  • kawigiedit
  • kindagoodday
  • lamebook
  • languages
  • lego
  • listedlinks
  • marathon
  • nasa
  • offtopic
  • ouch
  • postmortem
  • postportem
  • practical
  • probably_not_a_good_tip
  • problemsetting
  • programming
  • python
  • quora
  • rant
  • recap
  • slightlygoodday
  • snippet
  • srm
  • stl
  • strategy
  • swerc
  • tco
  • tco12
  • tco13
  • tco2012
  • tco2013
  • ternarysearch
  • topcoder
  • tricks
  • ubuntu
  • uva
  • vjass
  • vkcup
  • wc3
  • zinc

Blog Archive

  • ►  2014 (1)
    • ►  January (1)
  • ►  2013 (141)
    • ►  December (14)
    • ►  November (8)
    • ►  October (13)
    • ►  September (11)
    • ►  August (14)
    • ►  July (15)
    • ►  June (13)
    • ►  May (13)
    • ►  April (12)
    • ►  March (11)
    • ►  February (11)
    • ►  January (6)
  • ►  2012 (94)
    • ►  December (5)
    • ►  October (6)
    • ►  September (8)
    • ►  August (6)
    • ►  July (3)
    • ►  June (5)
    • ►  May (8)
    • ►  April (10)
    • ►  March (20)
    • ►  February (16)
    • ►  January (7)
  • ▼  2011 (51)
    • ▼  December (7)
      • SRM 528: System tests delayed again
      • It's unnamed holiday day
      • The minifig problem
      • SRM 527: Slooow
      • No more link spam, for now
      • SRM 526: The killing wait for results
      • Codeforces round #96 (division 2)
    • ►  November (12)
    • ►  October (5)
    • ►  September (1)
    • ►  August (3)
    • ►  July (4)
    • ►  June (3)
    • ►  May (7)
    • ►  April (3)
    • ►  March (2)
    • ►  February (1)
    • ►  January (3)
  • ►  2010 (9)
    • ►  December (4)
    • ►  October (1)
    • ►  June (1)
    • ►  May (1)
    • ►  January (2)
  • ►  2009 (1)
    • ►  December (1)
Powered by Blogger.

About Me

Unknown
View my complete profile