Search code examples
c++algorithmrecursionhashmapc++14

How to solve USACO 2013-Perimeter-Silver Recursively


So I was solving this USACO 2013 February Silver Contest - Perimeter - Problem 1.

Link to the problem: Problem Link

Link to the bronze version of this problem: Problem Link

Link to solutions: Silver - Link to Solution Bronze - Link to Solution

The problem:

Problem 1: Perimeter [Brian Dean, 2013]

Farmer John has arranged N hay bales (1 <= N <= 50,000) in the middle of one of his fields. If we think of the field as a 1,000,000 x 1,000,000 grid of 1 x 1 square cells, each hay bale occupies exactly one of these cells (no two hay bales occupy the same cell, of course).

FJ notices that his hay bales all form one large connected region, meaning that starting from any bale, one can reach any other bale by taking a series of steps either north, south, east, or west onto directly adjacent bales. The connected region of hay bales may however contain "holes" -- empty regions that are completely surrounded by hay bales.

Please help FJ determine the perimeter of the region formed by his hay bales. Note that holes do not contribute to the perimeter.

PROBLEM NAME: perimeter

INPUT FORMAT:

  • Line 1: The number of hay bales, N.

  • Lines 2..1+N: Each line contains the (x,y) location of a single hay bale, where x and y are integers both in the range 1..1,000,000. Position (1,1) is the lower-left cell in FJ's field, and position (1000000,1000000) is the upper-right cell.

SAMPLE INPUT (file perimeter.in):

8

10005 200003

10005 200004

10008 200004

10005 200005

10006 200003

10007 200003

10007 200004

10006 200005

INPUT DETAILS:

The connected region consisting of hay bales looks like this:

XX

X XX

XXX

OUTPUT FORMAT:

  • Line 1: The perimeter of the connected region of hay bales.

SAMPLE OUTPUT (file perimeter.out):

14

OUTPUT DETAILS:

The length of the perimeter of the connected region is 14 (for example, the left side of the region contributes a length of 3 to this total). Observe that the hole in the middle does not contribute to this number.

What I did

I went ahead with a recursive solution to the problem which goes like this :

#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;

#define rep(i,a,b) for(auto (i)=a;i<b;i++)
#define list(i,N) for(auto (i)=0;i<N;i++)

typedef long long ll;
typedef vector<ll> vi;
typedef pair<ll,ll> pi;
#define mp make_pair
#define pb push_back

#define int ll
#define INF 1e18+5
#define mod 1000000007
//One map for storing whether a cell has hay bale or not
//And the other for visited - whether a cell has been visited or not
map<pi,bool> vis;
map<pi,bool> exists;
int ans = 0;

void solve(int i, int j){
    //Check about the visited stuff
    if(vis[mp(i,j)]) return;
    vis[mp(i,j)] = true;
    //Find the answer now
    ans += 4;
    if(exists[mp(i-1,j)]){
        --ans; solve(i-1,j);    
    }
    if(exists[mp(i+1,j)]){
        --ans; solve(i+1,j);
    }
    if(exists[mp(i,j+1)]){
        --ans; solve(i,j+1);
    }
    if(exists[mp(i,j-1)]){
        --ans; solve(i,j-1);
    }
}

int32_t main(){

    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    int N; cin >> N;
    int first, second; //the starting point where we start the function...
    while(N--){
        int a,b; cin >> a >> b;
        first = a; second = b; //in the end, it is just the coordinate specified in the last in the input...
        exists[mp(a,b)] = true; //Hay Bale exists...
    }

    solve(first,second);
    cout << ans << "\n";

    return 0;
}

Basically, what I am doing is :

  1. Start at a cell.

  2. First, check if the cell has been previously visited. If yes, return. If not, make it visited.

  3. Add 4 to the counter for all the four sides.

  4. Look around the cell to all its neighbouring cells. If the cell also has haybales, subtract 1 from the counter (no need for adding boundary) and then goto 2.

The problem which I am facing

Observe that this code also counts the boundary required inside the hole. BUT we don't need to include that into our answer. I, however, don't know how to exclude that from our answer...

Why I mentioned the Bronze Problem

If you see the solution of the Bronze Problem (which is just the same problem but with different constraints), Mr Brian Dean also implements this sort of a recursive solution here which is similar to what I'm doing in my code. The code is down below :

#include <stdio.h>
#define MAX_N 100

int already_visited[MAX_N+2][MAX_N+2];
int occupied[MAX_N+2][MAX_N+2];
int perimeter;

int valid(int x, int y)
{
  return x>=0 && x<=MAX_N+1 && y>=0 && y<=MAX_N+1;
}

void visit(int x, int y)
{
  if (occupied[x][y]) { perimeter++; return; }
  if (already_visited[x][y]) return;
  already_visited[x][y] = 1;
  if (valid(x-1,y)) visit(x-1,y);
  if (valid(x+1,y)) visit(x+1,y);
  if (valid(x,y-1)) visit(x,y-1);
  if (valid(x,y+1)) visit(x,y+1);
}

int main(void)
{
  int N, i, x, y;

  freopen ("perimeter.in", "r", stdin);
  freopen ("perimeter.out", "w", stdout);

  scanf ("%d", &N);
  for (i=0; i<N; i++) {
    scanf ("%d %d", &x, &y);
    occupied[x][y] = 1;
  }

  visit(0,0);

  printf ("%d\n", perimeter);
  return 0;
}

Why this solution does not work for the Silver

This is because the constraints in the Silver version of the problem which I'm solving has higher constraints but the same time limit. This times out the code.

So, I would be grateful if anybody could help me solve this problem in order to exclude the perimeter taken up by the hole in the middle.


Solution

  • Your solution is quite similar to the second one posted. But instead of walking on the bales, you walk on the perimeter:

    void solve(int i, int j){
        if(vis[mp(i,j)]) return;
        if(exists[mp(i,j)]) return;
        if(there_is_no_bale_next_to(i,j)) return; // consider all 8 directions
        vis[mp(i,j)] = true;
        ans ++;
        solve(i-1,j);    
        solve(i+1,j);
        solve(i,j+1);
        solve(i,j-1);
    }
    

    You first run solve on a point definitely on perimeter(like the westmost point).