题目:https://vjudge.net/contest/338569#problem/P
显然可以想到引爆花费最小需要选择能引爆尽量多的,如果A能引爆B,B能引爆C,一定是引爆A最优。
如果有一些点可以相互引爆,那么就引爆这些点里花费最小的。
“能互相引爆”这显然是一个强连通分量,因此用tarjan求强连通分量之后,对于每个拓扑序最开头(入度为0)的连通分量,选择花费最小的进行引爆,即可求出答案。
ac代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e3 + 5;
int _, n, kase;
struct Point {
ll x, y, r;
int c;
} p[maxn];
vector<int> G[maxn];
ll getDis(const Point &a, const Point &b) {
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
int dfn[maxn], low[maxn], scc[maxn], tot, scc_cnt;
stack<int> st;
void tarjan(int x) {
dfn[x] = low[x] = ++tot;
st.push(x);
for (auto v:G[x]) {
if (!dfn[v]) {
tarjan(v);
low[x] = min(low[x], low[v]);
} else if (!scc[v]) {
low[x] = min(low[x], dfn[v]);
}
}
if (dfn[x] == low[x]) {
++scc_cnt;
int v;
while (v != x) {
v = st.top();
st.pop();
scc[v] = scc_cnt;
}
}
}
void findScc() {
tot = scc_cnt = 0;
memset(dfn, 0, sizeof(dfn));
memset(scc, 0, sizeof(scc));
for (int i = 1; i <= n; ++i) {
if (!dfn[i]) {
tarjan(i);
}
}
}
int deg[maxn], cost[maxn];
int main() {
scanf("%d", &_);
while (_--) {
memset(deg, 0, sizeof(deg));
memset(cost, 0x3f, sizeof(deg));
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%lld%lld%lld%d", &p[i].x, &p[i].y, &p[i].r, &p[i].c);
p[i].r = p[i].r * p[i].r;
}
for (int i = 1; i <= n; ++i) {
G[i].clear();
for (int j = 1; j < i; ++j) {
if (getDis(p[i], p[j]) <= p[i].r) {
G[i].push_back(j);
}
if (getDis(p[j], p[i]) <= p[j].r) {
G[j].push_back(i);
}
}
}
findScc();
for (int i = 1; i <= n; ++i) {
for (auto v:G[i]) {
if (scc[v] != scc[i]) {
++deg[scc[v]];
}
}
cost[scc[i]] = min(cost[scc[i]], p[i].c);
}
int ans = 0;
for (int i = 1; i <= scc_cnt; ++i) {
if (!deg[i]) {
ans += cost[i];
}
}
printf("Case #%d: %d\n", ++kase, ans);
}
return 0;
}