HDU - 5934(强连通缩点)

Bomb

题目传送门
题意:有n个炸弹,给出这些炸弹的坐标,爆炸半径和爆炸花费,如果在这个炸弹的爆炸范围内有其他炸弹,那么这个炸弹也能被引爆并且不需要花费,求使得所有炸弹爆炸所需的最小花费。
思路:我们可以按照炸弹的位置距离大小关系建图,当炸弹i可以引爆炸弹j时,则有i->j。如此我们就可以通过强连通找到联通块,该连通块内的炸弹可以互相引爆。那么所有连通块内引爆炸弹所需的最小花费之和是否是答案?不是,这里我们忽略了一种情况,则是在两个连通块其中的一个连通块中有一个炸弹是可以引爆另一个连通块,如此一来,另一个连通块则无需花费。这时候我们就需要强连通缩点了,当其缩点后,其入度为0的点就是我们需要引爆的连通块。

#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <algorithm>
#include <functional>
#define inf 10000000
using namespace std;
typedef long long ll;
const int MAXN=5e6+10;
const int MAX=4000+10;
const double eps=1e-6;

struct NODE{
    ll x,y,r;
    int cost;
}node[MAX];

struct EDGE{
    int u,v,next;
}edge[MAXN];

int n,num,Time,scc,ans,flag=1;
int first[20000],dfn[MAX],low[MAX],belong[MAX];
bool vis[MAX];
int du[MAX],save[MAX];
stack<int>q;

void init(){
    ans=scc=num=0;
    Time=1;
    fill(save,save+n+1,inf);
    memset(vis,0,sizeof(vis));
    memset(du,0,sizeof(du));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(first,-1,sizeof(first));
    while(q.size()) q.pop();
}

void addedge(int u,int v){
    edge[num].u=u;
    edge[num].v=v;
    edge[num].next=first[u];
    first[u]=num++;
}

void tarjan(int u){
    dfn[u]=low[u]=Time++;
    q.push(u);
    vis[u]=1;
    for(int i=first[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        int v;
        do{
            v=q.top();q.pop();
            vis[v]=0;
            belong[v]=scc;
        }while(u!=v);
        scc++;
    }
}

void get_scc(){
    for(int i=1;i<=n;i++){
        if(!dfn[i])
            tarjan(i);
    }
}

void sovle(){
    get_scc();
    for(int i=0;i<num;i++){
        int u=edge[i].u;
        int v=edge[i].v;
        if(belong[u]!=belong[v]){
            du[belong[v]]++;
        }
    }
    for(int i=1;i<=n;i++){
        if(du[belong[i]]==0){
            save[belong[i]]=min(save[belong[i]],node[i].cost);
        }
    } 
    for(int i=0;i<scc;i++){
        if(du[i]==0)
            ans+=save[i];
    }    
    printf("Case #%d: %d\n",flag++,ans);
}

int main(){
    #ifdef ONLINE_JUDGE
    #else
    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    #endif

    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        init();
        for(int i=1;i<=n;i++){
            scanf("%lld%lld%lld%d",&node[i].x,&node[i].y,&node[i].r,&node[i].cost);            
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==j)    continue;
                ll far=(node[i].x-node[j].x)*(node[i].x-node[j].x);
                far+=(node[i].y-node[j].y)*(node[i].y-node[j].y);
                if(far<=node[i].r*node[i].r){
                    addedge(i,j);
                }
            }
        }
        sovle();
    }

    return 0;   
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值