HDU

题意:给出你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;
}
原文地址:https://www.cnblogs.com/Tianwell/p/11317226.html