Hey guys. I'm having a lot of trouble with problem 109 (scud busters).
I have an AC version of it written in C++, but unfortunately, the logic it follows is nothing like mine. However, my program and the AC program matches exactly on every sample input I could find.
I have tested cases where the missiles land on the corner of a kingdom, cases where the missiles land on the kingdom wall, cases where missiles land within 0.001 units inside/outside the wall.
Argh...!!! Would someone be able to take a quick look at my (sloppy) code and tell me where my problem is???
Thanks!!
PS. Since gcj sucks, I had to build my own Stack and NumberFormatter, that's why there's a lot of code. Also, I know my sorting routine sucks, but it works in less than a second so it suffices.
class Main {
static Polygon[] kingdoms = new Polygon[20];
public static void main(String[] args) throws IOException {
int nhouses, nkingdoms = 0;
// read in points, get CH, add to array
while((nhouses = Integer.parseInt(readLine(16))) != -1) {
Point[] houses = new Point[nhouses];
for(int i = 0; i < nhouses; i++) {
StringTokenizer st = new StringTokenizer(readLine(16));
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
Point point = new Point(x, y);
houses = point;
}
Polygon polygon = Polygon.convexHull(houses, nhouses);
kingdoms[nkingdoms++] = polygon;
}
// read in missiles, check for intersection
int pointCount = 0;
String in;
while((in = readLine(16)) != null) {
StringTokenizer st = new StringTokenizer(in);
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
Point p = new Point(x, y);
for(int i = 0; i < nkingdoms; i++)
if(kingdoms.containsPoint(p)) kingdoms.destroy();
}
// get total area
double area = 0.0;
for(int i = 0; i < nkingdoms; i++)
if(kingdoms.isBombed()) area += kingdoms.getArea();
System.out.println(truncate(area, 2));
}
// used in place of NumberFormat
static String truncate(double s, int places) {
String ret = new String("0.");
if(s == 0) {
for(int i = 0; i < places; i++) ret += "0";
return ret;
}
ret = Long.toString(Math.round(s * Math.pow(10, places)));
ret = ret.substring(0, ret.length() - places) + "." +
ret.substring(ret.length() - places);
return ret;
}
static String readLine(int maxLg) throws IOException {
byte[] lin = new byte[maxLg];
int lg = 0, car = -1;
// <0 if right, =0 if on line, >0 if left
private static double isLeft(Point p0, Point p1, Point p2) {
return (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y);
}
The online judge does not use negatives. I put a check in my program that would enter an infinite loop if a negative was encountered (thus giving me a TLE). It gave me WA so I know there's no negatives.
I get TLE. If I put it right after, I get a WA. I'm thinking this is a weird compiler bug.. ? Also, notice there is no output in the convexhull method.
// A city is defined by its coordinates
typedef struct CITY
{
uint16 u16X; // The x location of the city
uint16 u16Y; // The y location of the city
}LOCATION;
// The kingdom class
class CKingdom
{
private:
bool m_bBombarded; // Has the kingdom been bombarded?
CITY *m_pCities; // The border cities
uint8 m_u8TotCities; // The total amount of border cities
public:
// Function prototypes
CKingdom();
~CKingdom();
void AddCity(const CITY *pCity);
double Area() const;
bool Bombard(const LOCATION *pLoc);
bool DeleteCity(uint8 u8Pos);
bool InsertCity(uint8 u8Pos, const CITY *pCity);
bool IsBombarded() { return m_bBombarded; }
bool IsLocationWithinBorders(const LOCATION *pLoc) const;
bool IsSharpAngle(const CITY *pCityA, const CITY *pCityB, const CITY *pCityC) const;
void PrintPath() const;
};
// Uncomment the following line to show debug info
//#define TEST
// The global variables
CKingdom **g_ppKingdoms; // The kingdoms
uint8 g_u8TotKingdoms; // The total amount of kingdoms
//---------------------------------------------------------------------------
// F U N C T I O N S
//---------------------------------------------------------------------------
// This function is the entrance of the app
int main()
{
double dBombardedArea = 0.0;
int16 i16TotCities;
LOCATION loc;
// Get the specifications of the kingdoms
while(scanf("%hi", &i16TotCities) == 1 && i16TotCities != -1)
{
// Add a new kingdom
CKingdom **ppTemp;
uint8 u8City;
// Augment the amount of kingdoms with 1
ppTemp = new CKingdom*[g_u8TotKingdoms+1];
memcpy(ppTemp, g_ppKingdoms, g_u8TotKingdoms * sizeof(CKingdom *));
SAFE_DELETE_ARRAY(g_ppKingdoms);
g_ppKingdoms = ppTemp;
g_ppKingdoms[g_u8TotKingdoms] = new CKingdom;
g_u8TotKingdoms++;
// Get the cities
for(u8City=0;u8City<i16TotCities;u8City++)
{
CITY city;
// Add the new city
scanf("%hu %hu", &city.u16X, &city.u16Y);
g_ppKingdoms[g_u8TotKingdoms-1]->AddCity(&city);
#ifdef TEST
// Show the new kingdom
printf("(%2u,%2u) -> ", city.u16X, city.u16Y);
g_ppKingdoms[g_u8TotKingdoms-1]->PrintPath();
#endif
}
#ifdef TEST
// Show the area of the kingdom
printf("Area = %f\n\n", g_ppKingdoms[g_u8TotKingdoms-1]->Area());
#endif
}
// Handle the missile attacks
while(scanf("%hu %hu", &loc.u16X, &loc.u16Y) == 2)
{
#ifdef TEST
// Show the missile's location
printf("Missile: (%hu,%hu)\n", loc.u16X, loc.u16Y);
#endif
// Check which kingdoms are hit by the missile
for(uint8 u8Kingdom=0;u8Kingdom<g_u8TotKingdoms;u8Kingdom++)
{
// Has this kingdom not been hit yet?
if(!g_ppKingdoms[u8Kingdom]->IsBombarded())
{
// Bombard the kingdom
g_ppKingdoms[u8Kingdom]->Bombard(&loc);
#ifdef TEST
// Has it been a hit?
if(g_ppKingdoms[u8Kingdom]->IsBombarded())
printf("Kingdom %u has been hit\n", u8Kingdom);
#endif
}
}
}
// Calculate the area of the kingdoms that have been bombarded
for(uint8 u8Kingdom=0;u8Kingdom<g_u8TotKingdoms;u8Kingdom++)
{
// Has the kingdom been bombarded?
if(g_ppKingdoms[u8Kingdom]->IsBombarded())
dBombardedArea += g_ppKingdoms[u8Kingdom]->Area();
}
// Show the total area which has (successfully) been bombarded
#ifdef TEST
printf("Bombarded area = ");
#endif
printf("%.2f\n", dBombardedArea);
// Free the resources
for(uint8 u8=0;u8<g_u8TotKingdoms;u8++) SAFE_DELETE(g_ppKingdoms[u8]);
SAFE_DELETE_ARRAY(g_ppKingdoms);
// This function adds a new city to the kingdom
void CKingdom::AddCity(const CITY *pCity)
{
// Do we currently have 0 or 1 cities?
if(m_u8TotCities < 2)
{
// Add the city
InsertCity(m_u8TotCities, pCity);
}
// Do we currently have 2 cities?
else if(m_u8TotCities == 2)
{
// Fit the new city in clockwise order
if(IsSharpAngle(&m_pCities[0], pCity, &m_pCities[1])) InsertCity(1, pCity);
else InsertCity(2, pCity);
}
else
{
uint8 u8City;
// Find a fit
for(u8City=0;u8City<m_u8TotCities;u8City++)
{
// Does the new city fit between this city and the next?
if(IsSharpAngle(&m_pCities[u8City], pCity, &m_pCities[(u8City+1) % m_u8TotCities])) break;
}
// Did we find a fit?
if(u8City != m_u8TotCities)
{
bool bNonFitting;
// Insert the new city
InsertCity(u8City+1, pCity);
// Prune the non-fitting cities
do
{
// Check all adjoining borders
bNonFitting = false;
for(u8City=0;u8City<m_u8TotCities;u8City++)
{
// Doesn't the middle city fit?
if(!IsSharpAngle(&m_pCities[u8City], &m_pCities[(u8City+1) % m_u8TotCities], &m_pCities[(u8City+2) % m_u8TotCities]))
{
// Remove the city
DeleteCity((u8City+1) % m_u8TotCities);
bNonFitting = true;
break;
}
}
}while(bNonFitting);
}
}
}
// This function bombards the kingdom
bool CKingdom::Bombard(const LOCATION *pLoc)
{
// Is the location within the borders of the kingdom?
if(IsLocationWithinBorders(pLoc)) m_bBombarded = true;
// This function removes the specified city
bool CKingdom::DeleteCity(uint8 u8Pos)
{
bool bResult = false;
// Do we have a legal position?
if(u8Pos <= m_u8TotCities)
{
// Don't reallocate memory. Simply don't use the last of the allocated cities
if(u8Pos < m_u8TotCities) memcpy(&m_pCities[u8Pos], &m_pCities[u8Pos+1], (m_u8TotCities-u8Pos-1) * sizeof(CITY));
m_u8TotCities--;
// This function determines whether the angle between 2 borders is smaller than 180 degrees
bool CKingdom::IsSharpAngle(const CITY *pCityA, const CITY *pCityB, const CITY *pCityC) const
{
// Is the angle between 0 and 180 degrees?
return (pCityA->u16X - pCityB->u16X) * (pCityC->u16Y - pCityB->u16Y) -
(pCityA->u16Y - pCityB->u16Y) * (pCityC->u16X - pCityB->u16X) > 0;
}
// This function determines whether the specified location falls within the borders of the kingdom
bool CKingdom::IsLocationWithinBorders(const LOCATION *pLoc) const
{
uint8 u8City;
// Check whether the location would fit the polygon
for(u8City=0;u8City<m_u8TotCities;u8City++)
{
// Does the location fit between this city and the next?
if(IsSharpAngle(&m_pCities[u8City], pLoc, &m_pCities[(u8City+1) % m_u8TotCities])) break;
}
// This function shows the clockwise border walk
void CKingdom::PrintPath() const
{
// Print the border cities in clockwise order
for(uint8 u8City=0;u8City<m_u8TotCities;u8City++) printf("(%2u,%2u) ", m_pCities[u8City].u16X, m_pCities[u8City].u16Y);
printf("\n");
}
[/cpp]
Ok, I got accepted. The problem was the calculation of the angle.
To make the source code Accept, change the IsSharpAngle function to:
[cpp]// This function determines whether the angle between 2 borders is smaller than 180 degrees
bool CKingdom::IsSharpAngle(const CITY *pCityA, const CITY *pCityB, const CITY *pCityC) const
{
// Is the angle between 0 and 180 degrees?
return (pCityA->u16X - pCityB->u16X) * (pCityC->u16Y - pCityB->u16Y) -
(pCityA->u16Y - pCityB->u16Y) * (pCityC->u16X - pCityB->u16X) >= 0;
} [/cpp]