题意:给出你N个炸弹坐标,以及每个的爆炸范围,引爆炸弹所需的花费。如果某个炸弹在另一个的爆炸范围以内,则可以被另一个炸弹引爆(间接引爆)。问引爆所有炸弹所需的最小花费
思路:把炸弹看作一个点,间接引爆就相当于一条有向边。最终所形成的图中,我们去找强连通分量。每个分量中花费最小的cost 以及 入度为0的 炸弹cost之和 即为所求。
这里我们使用tarjan来缩点求强连通分量。
完整代码:(注意 要开long long)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
#include <cmath>
#define ll long long
#define IOS ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
const int maxn = 1e3+3;
const int maxe = 1e6+1;
struct Edge{
int v,next;
}edge[maxe];
struct Bomb{
ll x,y;
ll c;
double r;
}bomb[maxn];
int head[maxn];
int indeg[maxn],outdeg[maxn];
int dfn[maxn],low[maxn];
int belong[maxn],instack[maxn];
int top,idex,num,n;
stack<int>S;
void init(){
top = idex = num = 0;
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(indeg,0,sizeof(indeg));
memset(instack,0,sizeof(instack));
memset(belong,0,sizeof(belong));
}
void add(int u,int v){
edge[top].v = v;
edge[top].next = head[u];
head[u] = top++;
}
void tarjan(int x){
dfn[x] = low[x] = ++idex;
instack[x] = 1;
S.push(x);
for(int i = head[x]; ~i ;i =edge[i].next){
int v = edge[i].v;
if(!dfn[v]){
tarjan(v);
low[x] = min(low[x],low[v]);
}else if(instack[v]){
low[x] = min(low[x],dfn[v]);
}
}
if(low[x] == dfn[x]){
int t;
++num;
do{
t = S.top();
belong[t] = num;
instack[t] = 0;
S.pop();
}while(t != x);
}
}
bool check(int i, int j) {
return 1LL * sqrt(1LL * (bomb[i].x - bomb[j].x) * (bomb[i].x - bomb[j].x) + 1LL *
(bomb[i].y - bomb[j].y) * (bomb[i].y -bomb[j].y)) <= bomb[i].r;
}
int main(){
IOS;
int T,Case;
cin>>T;
Case = 0;
while(T--){
init();
cin>>n;
for(int i =1;i <= n ;i++){
cin>>bomb[i].x>>bomb[i].y>>bomb[i].r>>bomb[i].c;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) if (i != j && check(i, j))
add(i, j);
for(int i =1;i <= n;i++){
if(!dfn[i]) tarjan(i);
}
//遍历边判断两点是否在同一个连通分量中,如果在则入度加1
for(int i =1;i <= n;i++){
for(int j = head[i]; ~j ;j = edge[j].next){
if(belong[i] == belong[edge[j].v]) continue;
indeg[belong[edge[j].v]]++;//记录入度
}
}
ll ans;
ll res;
ans = 0;
for(int i = 1; i<= num; i++){
if(!indeg[i]){
res = 1e18;
//找到该分量中最小的cost
for(int j = 1;j<=n;j++){
if(belong[j] == i) res = min(res,bomb[j].c);
}
ans += res;
}
}
cout<<"Case #"<<++Case<<": "<<ans<<endl;
}
return 0;
}