Problem E
Battle II
Time Limit: 6 seconds
"Marriage, in life, is like a duel in the |
Edmond About
Daltons (Daida, Alhar, Tara, and Reyton) love playing games. One of their favorite games is 'Battle II'. In order to play this game, first, one of them is chosen as a problem-setter. The problem-setter starts drawing some bombs on a piece of paper (each bomb is a circle: has a center and a radius). She then associates to each bomb a destruction range. There are three rules defined in this game:
- If a bomb explodes, all the bombs in its destruction range will also explode. A bomb b1 is in destruction range of another bomb b2 if distance of b2 from the perimeter of b1 is less than the range of b1. (i.e. b2.range + b1.radius + b2.radius >= Distance(b1.center, b2.center))
- A bomb may explode due to either being affected by explosion of another bomb according to the first rule or manually being fired.
- Firing a bomb manually has a cost which is equal to the range of it.
She finally gives the configuration of the bombs to the others and asks them to find a sequence of bombs to fire which should satisfy the following conditions:
- All the bombs should be exploded as a result of firing and explosion of the bombs in this sequence.
- The ith bomb in the sequence should not result explosion of the jth bomb where j > i.
- The average cost of firing the bombs that are in the sequence must be minimum.
You should help the players find the solution to this problem by writing a program which is able to find such a sequence given the specifications and configuration of the bombs in the paper.
Input
The first line of input gives the number of cases, N. N test cases will follow. Each one starts with a line containing the number of bombs (
0<
n<=300
). Each of the next n lines contains four integers Xi, Yi, Ri, Ei, meaning that the i-th bomb is located at (Xi, Yi), has a radius of Ri, and has a range of Ei. There will be a blank line after each block of test case.
Output
For each test case, output the line containing "Case #x:", followed by list of bombs in the order that should be fired, seperated by a single space. Follow the output format used in sample output. If there are more than one solution, any of them is acceptable.
Sample Input Output for Sample Input
1 3 4 7 2 2 8 5 1 0 3 -3 1 1 | Case #1: 1 0 2 |
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<math.h>
using namespace std;
#define eps 1e-8
#define LL long long
const int maxn = 300+5;
struct Bomb
{
int x , y , r , e;
int index;
}bomb[maxn];
inline bool operator < (const Bomb & b1 , const Bomb & b2)
{
if (b1.e==b2.e) return b1.index < b2.index;
return b1.e < b2.e;
}
struct Ans
{
Ans(int dh,int ct,int ix,int no) : depth(dh) , cost(ct) , index(ix) , sccno(no) { }
int depth;
int cost;
int index;
int sccno;
};
inline bool cmp_cost(const Ans & a1,const Ans & a2)
{
if (a1.cost==a2.cost) return a1.index < a2.index;
return a1.cost < a2.cost;
}
inline bool cmp_depth(const Ans & a1,const Ans & a2)
{
if (a1.depth==a2.depth) return a1.index > a2.index;
return a1.depth > a2.depth;
}
int n , dfn , scc_cnt , sccno[maxn] , pre[maxn] , low[maxn];
int ind[maxn]; //入度(用于拓扑排序)
bool vis[maxn];
vector<int> G1[maxn]; //原图
vector<int> G2[maxn]; //缩点图
stack<int> S;
vector<Bomb> scc[maxn]; //强连通分量的点
inline LL sqr(LL x) { return x*x; }
inline LL dist(int i,int j) { return sqr(bomb[i].x-bomb[j].x)+sqr(bomb[i].y-bomb[j].y); }
inline bool connect(int i,int j)
{
return dist(i,j) <= sqr(bomb[i].r+bomb[j].r+bomb[i].e);
}
void input()
{
for (int i = 0 ; i < n ; ++i) G1[i].clear();
for (int i = 0 ; i < n ; ++i)
{
scanf("%d%d%d%d",&bomb[i].x,&bomb[i].y,&bomb[i].r,&bomb[i].e);
bomb[i].index = i;
}
for (int i = 0 ; i < n ; ++i) {
for (int j = 0 ; j < n ; ++j) if (i!=j && connect(i,j))
{
G1[i].push_back(j);
}
}
}
void dfs(int u)
{
pre[u] = low[u] = ++dfn;
S.push(u);
for (int i = 0 ; i < G1[u].size() ; ++i)
{
int v = G1[u][i];
if (!pre[v])
{
dfs(v);
low[u] = min(low[u],low[v]);
} else if (!sccno[v])
low[u] = min(low[u],pre[v]);
}
if (pre[u]==low[u]) {
++scc_cnt;
scc[scc_cnt].clear();
G2[scc_cnt].clear();
while (true)
{
int x = S.top(); S.pop();
sccno[x] = scc_cnt;
scc[scc_cnt].push_back(bomb[x]);
if (x==u) break;
}
}
}
//找强连通分量并缩点构图
void find_scc()
{
dfn = scc_cnt = 0;
memset(pre,0,sizeof(pre));
memset(low,0,sizeof(low));
memset(sccno,0,sizeof(sccno));
for (int i = 0 ; i < n ; ++i) if (!pre[i])
dfs(i);
memset(ind,0,sizeof(ind));
for (int i = 1 ; i <= scc_cnt ; ++i)
{
memset(vis,0,sizeof(vis));
vis[i] = true;
for (int j = 0 ; j < scc[i].size() ; ++j) {
int u = scc[i][j].index;
for (int k = 0 ; k < G1[u].size() ; ++k) {
int v = G1[u][k];
if (vis[sccno[v]]) continue;
vis[sccno[v]] = true;
G2[sccno[u]].push_back(sccno[v]);
++ind[sccno[v]];
}
}
}
}
void solve()
{
find_scc();
memset(vis,0,sizeof(vis));
vector<Ans> ans1;
for (int i = 1 ; i <= scc_cnt ; ++i) sort(scc[i].begin(),scc[i].end());
stack<int> out;
int sum = 0;
int cnt = 0;
queue<Ans> q;
for (int i = 1 ; i <= scc_cnt ; ++i)
{
if (ind[i]==0) {
sum += scc[i][0].e;
++cnt;
q.push(Ans(0,scc[i][0].e,scc[i][0].index,i));
out.push(scc[i][0].index);
}
}
while (q.size())
{
Ans tmp = q.front(); q.pop();
if (tmp.depth) ans1.push_back(tmp);
int u = tmp.sccno;
for (int i = 0 ; i < G2[u].size() ; ++i)
{
int v = G2[u][i];
--ind[v];
if (ind[v]==0) q.push(Ans(tmp.depth+1,scc[v][0].e,scc[v][0].index,v));
}
}
sort(ans1.begin(),ans1.end(),cmp_cost);
vector<Ans> ans2;
for (int i = 0 ; i < ans1.size() ; ++i)
{
if (sum > cnt * ans1[i].cost)
{
sum += ans1[i].cost;
++cnt;
ans2.push_back(ans1[i]);
}
}
sort(ans2.begin(),ans2.end(),cmp_depth);
for (int i = ans2.size()-1 ; i >= 0 ; --i) out.push(ans2[i].index);
while (out.size())
{
printf(" %d",out.top());
out.pop();
}
cout << endl;
}
int main()
{
int T; cin>>T;
int k = 0;
while (T--)
{
++k;
scanf("%d",&n);
input();
printf("Case #%d:",k);
solve();
}
}