// RoadConstruction.cpp : Defines the entry point for
the console application.
//
// by Adrian Dale 15/12/2008
// Solution to the IEEE Challenge 2008 - Road
Construction
//
http://www.ieee.org/portal/cms_docs_iportals/iportals/membership/students/scholarshipsawardscontests/IEEEXtreme2008_Competition_Booklet.pdf
//
// This code works by first parsing the input data
into a minimal set of constraints
// needed to solve the problem. That is, we only need
to take into account the houses
// that are closest to each side of the road. So, for
each row we store the widest allowed
// distance from the road to the left of the road and
to the right of the road.
//
// We then use a recursive function to try out all
possible starting positions of
// each parcel of land to see what area of land they
give.
// We record the maximum area once all possible parcel
subdivisions have been tested
// and output it as the puzzle answer.
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <algorithm>
using namespace
std;
// Data structures to hold the road map data
// We don't need to hold a 2D array, since some of the
house data is likely
// to be redundant.
// All we need to know for each row is the smallest
possible rectangle we
// could fit on that row
struct BoardRow
{
int
Left;
int
Right;
};
vector<BoardRow> Board;
typedef
vector<BoardRow>::iterator BoardIter;
// We get given this number - the number of parcels of
land we must divide the
// map into
int Parcels = 0;
// We get given this number - Height is how long the
road is
int Height = 0;
int Width = 0;
// We need this to store the maximum area so far as we
go through our recursive
// algorithm.
int MaxParcelArea = 0;
// ReadInput reads the challenge parameters from
stdin.
// See challenge for input format.
// Note that this function assumes the input data is
valid.
// Not a great assumption but should do for this quick
and dirty code.
void ReadInput()
{
int
Houses = 0;
//
Height of the board comes first in the input
cin >> Height;
//
We'll have a row for each square of height
Board.resize(Height);
//
Next input value is width
cin >> Width;
//
Width of map marks the min and max possible widths of a rectangle
//
at each y coordinate. Note that a Width of n is equivalent to a house at n+1
//
Adding in the houses will narrow these constraints
for(
BoardIter it=Board.begin(); it != Board.end(); ++it )
{
it->Left = -1 * (Width+1);
it->Right = Width+1;
}
//
Read in count of number of parcels of land
cin >> Parcels;
//
Read in the number of houses - we use this to tell us how many
//
sets of input coordinates to expect
cin >> Houses;
for(
int i=0; i<Houses; ++i )
{
int HouseX = 0;
int HouseY = 0;
cin >> HouseY;
cin >> HouseX;
// Decrement HouseY so we can use it
as an array index.
// For some reason the rows seem to
// be numbered from 1 in the input.
--HouseY;
if ( HouseX < 0 )
{
// Only update the board with the
house coordinates if it narrows the
// constraints
if ( HouseX >
Board[HouseY].Left)
Board[HouseY].Left =
HouseX;
}
else
{
if ( HouseX <
Board[HouseY].Right)
Board[HouseY].Right
= HouseX;
}
}
}
// GetParcelArea returns the area covered by the
passed in
// allocation of the parcels
int GetParcelArea( vector<int> &ParcelList )
{
int
TotalParcelArea = 0;
//
Parcel list comes in as a vector of starting positions
//
for each parcel, numbered from 0.
//
Go through each parcel in the list and add its area to the
//
TotalParcelArea
for
( int parcelNo=0;
parcelNo<ParcelList.size(); ++parcelNo )
{
// We're given the start row of each
parcel.
// The end row is the one before the
start of the next parcel,
// or the top of the map for the last
parcel
int ParcelStart =
ParcelList[parcelNo];
int ParcelEnd;
if ( parcelNo <
ParcelList.size() - 1 )
ParcelEnd = ParcelList[parcelNo+1];
else
ParcelEnd = Height;
// Work out the widest rectancle we
can fit in this parcel's
// range of rows by looping through
the constraints for each
// row that makes up the parcel
int MaxLeft = -1*(Width+1);
int MinRight = Width+1;
for( int
i=ParcelStart; i<ParcelEnd; ++i )
{
if ( Board[i].Left >
MaxLeft)
MaxLeft =
Board[i].Left;
if ( Board[i].Right <
MinRight)
MinRight =
Board[i].Right;
}
// Very simple formula for area of a
rectangle!
int ParcelArea = (MinRight -
MaxLeft - 1) * (ParcelEnd - ParcelStart);
TotalParcelArea += ParcelArea;
}
return
TotalParcelArea;
}
// This function recursively tries all possible parcel
// sizes into the parcel list
void TryParcelList( vector<int> ParcelList, int
parcelNo )
{
if
( parcelNo == Parcels )
{
// We've filled our list of guesses,
so try it out
int GuessArea = GetParcelArea(
ParcelList );
if ( GuessArea >
MaxParcelArea )
MaxParcelArea = GuessArea;
}
else
{
// Still some more parcels to
allocate, so call this fn recursively
// for each possible parcel left at
this level, starting from
// where the previous ones left off,
and finishing with enough room for
// all remaining parcels of minimum
size
for (int
i=ParcelList[parcelNo-1] + 1; i <= Height - Parcels + parcelNo; ++i )
{
ParcelList[parcelNo] = i;
TryParcelList( ParcelList, parcelNo + 1 );
}
}
}
// This function kicks off the recursive search for
the best possible subdivision
// of the map into parcels.
int GetMaxParcelArea()
{
vector<int> ParcelList;
ParcelList.resize(Parcels);
MaxParcelArea = 0;
//
First piece always starts at zero
ParcelList[0] = 0;
//
Kick off the recursive function
TryParcelList( ParcelList, 1
);
return
MaxParcelArea;
}
int _tmain(int argc, _TCHAR* argv[])
{
ReadInput();
cout <<
GetMaxParcelArea() << endl;
return
0;
}