题意:给出n个炸弹坐标、爆炸范围、爆炸费用,如果某一个炸弹在另一个炸弹的爆炸范围内(上),那么该炸弹也能爆炸,问引爆所有炸弹最小的费用
思路:强连通分量分解,将所有点缩点,然后引爆所有入度为0的点即为最小费用
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
typedef long long ll;
const int maxn = 1e3 + 10;
const ll INF = 1e18;
using namespace std;
vector<int> G[maxn];
vector<int> rG[maxn];
vector<int> vs;
int use[maxn], topo[maxn];
int n, m, s, T, kase = 1;
ll x[maxn], y[maxn], r[maxn];
ll c[maxn], cost[maxn];
int res[maxn];
void add(int f, int t) {
G[f].push_back(t);
rG[t].push_back(f);
}
void dfs(int u) {
use[u] = 1;
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if(!use[v]) dfs(v);
}
vs.push_back(u);
}
void rdfs(int u, int k) {
use[u] = 1; topo[u] = k;
for(int i = 0; i < rG[u].size(); i++) {
int v = rG[u][i];
if(!use[v]) rdfs(v, k);
}
}
int scc() {
int k = 1;
memset(use, 0, sizeof(use));
vs.clear();
for(int i = 1; i <= n; i++) {
if(!use[i]) dfs(i);
}
memset(use, 0, sizeof(use));
for(int i = vs.size() - 1; i >= 0; i--) {
int v = vs[i];
if(!use[v]) rdfs(v, k++);
}
return k - 1;
}
bool cover(int i, int j) {
ll dx = (x[i] - x[j]) * (x[i] - x[j]);
ll dy = (y[i] - y[j]) * (y[i] - y[j]);
if(dx + dy <= r[i] * r[i]) return true;
return false;
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%lld %lld", &x[i], &y[i]);
scanf("%lld %lld", &r[i], &c[i]);
cost[i] = INF;
G[i].clear();
rG[i].clear();
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(i == j) continue;
if(!cover(i, j)) continue;
add(i, j);
}
}
int num = scc();
ll ans = 0;
for(int i = 1; i <= n; i++) {
cost[topo[i]] = min(c[i], cost[topo[i]]);
}
memset(use, 0, sizeof(use));
vs.clear();
for(int i = 1; i <= num; i++) {
for(int j = 1; j <= n; j++) {
if(topo[j] != i) continue;
if(use[j]) continue;
ans += cost[i];
dfs(j); break;
}
}
printf("Case #%d: %lld\n", kase++, ans);
}
return 0;
}