hdu5934-强连通分量-缩点-tarjan

题目链接:https://vjudge.net/problem/541918/origin

题意:有n个炸弹,位于(xi,yi),爆炸半径为ri,花费为ci。炸弹爆炸时,在其爆炸范围的炸弹都会被引爆,求最少花费使得所有的炸弹都引爆。 

如果A能引爆B,那么在A B之间建一条单向边A->B ,然后用Tarjan算法缩点,缩点过程把相连通分量内的那个花费最小的炸弹求出并保存,然后用缩点建立新图,每次引爆入度为0的炸弹即可,

#include<cstdio>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#define ll long long
using namespace std;
const int maxn=1010;
const int maxm=10005;
const int inf=1e9+10;
struct Edge
{
    int to,next;
}edge[maxn*maxn*2];
struct Point
{
    ll x,y,r;
    int val;
}p[maxn];
int head[maxn],tot;
int low[maxn],dfn[maxn],Stack[maxn],belong[maxn];
int index,top;
int scc;
int c[maxn];
int Case=1;

ll dis(Point p1,Point p2)
{
    return (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);
}
bool Instack[maxn];
void addedge(int u,int v)
{
    edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++;
}
void tarjan(int u)
{
    int v;
    low[u]=dfn[u]=++index;
    Stack[top++]=u;
    Instack[u]=true;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        v=edge[i].to;
        if(!dfn[v])
    {
        tarjan(v);
        if(low[u]>low[v])low[u]=low[v];
    }
    else if(Instack[v]&&low[u]>dfn[v])
        low[u]=dfn[v];
    }
    if(low[u]==dfn[u])
    {
        scc++;
        do{
            v=Stack[--top];
            Instack[v]=false;
            belong[v]=scc;
            c[scc]=min(c[scc],p[v].val);
        }
        while(v!=u);
    }
}
int in[maxn],out[maxn];
void solve(int n)
{
    memset(dfn,0,sizeof(dfn));
    memset(Instack,false,sizeof(Instack));
    memset(c,inf,sizeof(c));
    index=scc=top=0;
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
            tarjan(i);

    }
    for(int i=1;i<=scc;i++)
    {
        in[i]=out[i]=0;
    }
    for(int u=1;u<=n;u++)
    {
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(belong[u]!=belong[v]){

                in[belong[v]]++;


            }
        }
    }
    ll ans=0;
    for(int i=1;i<=scc;i++)
    {
        if(in[i]==0){
            {
                ans+=c[i];

            }
        }
    }
    printf("Case #%d: %lld\n",Case++,ans);
}
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld%lld%lld%lld",&p[i].x,&p[i].y,&p[i].r,&p[i].val);

        }
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(i==j)continue;
                ll d=dis(p[i],p[j]);
                if(d<=p[i].r*p[i].r)
                    {
                        addedge(i,j);
                        cnt++;

                    }
            }
        }
        solve(n);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谷丘CODER

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值