UVa11853
在一个正方形区域中有若干个敌人,每个敌人的攻击范围为r
,求是否可以在不被攻击的前提下横穿正方形区域。如果可以横穿,求出最靠近北侧的入口和出口。
看起来无从下手,但是画个图就明白了。如果没有可以相互掩护射击的敌人的攻击范围覆盖了从北到南的每一个点,那么就是可以横穿的——只需要从敌人攻击范围之间的缝隙穿过就可以了,所以可以根据是否存在从北到南的连通块判断是否存在解。
接下来就是找入口和出口了。如果某个连通块覆盖了北边界,那么可以看包含圆形的连通块是否覆盖了西边界,如果覆盖了,那么肯定是不能从西北区域进入了,只能从西北区域的南边界点进入;否者从西北角进入就可以了。根据这个思路,问题就转化为了求解所有覆盖北边界的连通块和西边界交点中最南侧的点。
至于uDebug上的用例,好像有些问题。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <sstream>
#include <vector>
using namespace std;
struct Opponent
{
double x, y, r;
bool cross(const Opponent &opponent) const
{
return pow(x - opponent.x, 2) + pow(y - opponent.y, 2)
< pow(r + opponent.r, 2);
}
};
struct Block
{
vector<Opponent> opponents;
double NorthBound, SouthBound;
Block(const Opponent &opponent)
{
NorthBound = opponent.y + opponent.r;
SouthBound = opponent.y - opponent.r;
opponents.push_back(opponent);
}
void AddOpponent(const Opponent &opponent)
{
NorthBound = max(opponent.y + opponent.r, NorthBound);
SouthBound = min(opponent.y - opponent.r, SouthBound);
opponents.push_back(opponent);
}
};
class Solution
{
public:
Solution(const vector<Opponent> &opponents) : visited(opponents.size(), false)
{
for (size_t i = 0; i < opponents.size(); i++)
{
if (!visited[i]) {
visited[i] = true;
blocks.push_back(Block(opponents[i]));
CountBlock(i, opponents);
}
}
}
string GetEntry()
{
double LeftY = 1000.0, RightY = 1000.0, y;
ostringstream oss;
for (const Block &block : blocks)
{
if (block.NorthBound > 1000.0) {
if (block.SouthBound < 0.0) return "IMPOSSIBLE";
else {
for (const Opponent &opponent : block.opponents)
{
if (opponent.x - opponent.r < 0.0) {
y = opponent.y - sqrt((pow(opponent.r, 2) - pow(opponent.x, 2)));
LeftY = min(y, LeftY);
}
if (opponent.x + opponent.r > 1000.0) {
y = opponent.y - sqrt((pow(opponent.r, 2) - pow(1000.0 - opponent.x, 2)));
RightY = min(y, RightY);
}
}
}
}
}
oss << fixed << setprecision(2);
oss << "0.00 " << LeftY << ' ' << "1000.00 " << RightY;
return oss.str();
}
private:
vector<Block> blocks;
vector<bool> visited;
void CountBlock(size_t curr, const vector<Opponent> &opponents)
{
for (size_t i = 0; i < opponents.size(); i++)
{
if (!visited[i] && opponents[curr].cross(opponents[i])) {
visited[i] = true;
blocks.back().AddOpponent(opponents[i]);
CountBlock(i, opponents);
}
}
}
};
int main()
{
size_t n = 0;
while (cin >> n) {
vector<Opponent> opponents;
for (size_t i = 0; i < n; i++)
{
Opponent opponent;
cin >> opponent.x >> opponent.y >> opponent.r;
opponents.push_back(opponent);
}
Solution solution(opponents);
cout << solution.GetEntry() << endl;
}
return 0;
}
/*
3
500 500 499
0 0 999
1000 1000 200
*/