题意:给出你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; }