hdu 5934 Bomb (强联通分量)模板

本文解析了HDU5934题目,通过构建有向图和使用强连通分量分析,将问题转化为寻找DAG中入度为0节点的最小花费路径。介绍了使用Tarjan算法进行强连通分量分析的过程,并提供了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:hdu 5934

参考博客:https://www.cnblogs.com/fightfordream/p/6093256.html

 

题意:有N个炸弹,每个炸弹有一个坐标,一个爆炸范围和一个爆炸花费,如果一个炸弹的爆炸范围内有另外的炸弹,那么如果该炸弹爆炸,就会引爆所有爆炸范围内的炸弹,求让所有炸弹爆炸的最小花费。

思路:先n^2的把每个炸弹爆炸范围内的炸弹都连一条有向边,然后再找强连通分量缩点,这样会形成多个DAG,然后对于每个DAG找一个入度为0的点,找这个入度为0的点里面耗费最小的去引爆,就可以了。

在DAG图里,入度不为0的点一定可以由某个入度为0的点到达(由于无环,所以从任何入度不为0的 点往回走,必然终止于一个入度为0的点)

 

代码:

还要注意一点的是,建边要是选择的方法不同,可能会超时,见代码注释那里,原因还未知。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;

typedef long long LL;
#define INF 0x3f3f3f3f
const int maxn=1010;

struct point{
    LL x,y,r,c; ///注意这里是long long 虽说数据不超过int,但在判断距离时可能会溢出
}p[maxn];

struct node{
    int u,v,nxt;
}edge[maxn*maxn]; ///开平方倍

int DFN[maxn],LOW[maxn],vis[maxn],head[maxn];
int belong[maxn],top,tot,cnt,stacks[maxn],index;
int out[maxn],in[maxn]; ///每个强联通分量块的出度,入度


void add(int u,int v){///建立边
    tot++;
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot].nxt=head[u];
    head[u]=tot;
}

///不要这样建边,会超时,原因未知
//void add(int x,int y)
//{
//    tot++;
//    nexts[tot]=point[x];
//    point[x]=tot;
//    v[tot]=y;
//}

void tarjan(int x)  ///模板
{
    stacks[++top]=x;
    vis[x]=1;

    DFN[x]=LOW[x]=++index;

    for(int i=head[x];i;i=edge[i].nxt)
    {
        int j=edge[i].v;
        if(!DFN[j]){
            tarjan(j);
            LOW[x]=min(LOW[x],LOW[j]);
        }
        else if(vis[j]) LOW[x]=min(LOW[x],LOW[j]);
    }

    if(DFN[x]==LOW[x])
    {
        cnt++;
        int item;

        do{
            item=stacks[top--];
            vis[item]=0;
            belong[item]=cnt;
        }while(item!=x);
    }
}

int dcmp(double x)
{
    if(fabs(x)<1e-8) return 0;
    else return x>0?1:-1;
}
bool dis(int u,int v){ ///判断是否可以连边
    double len=sqrt(((p[u].x-p[v].x)*(p[u].x-p[v].x)+(p[u].y-p[v].y)*(p[u].y-p[v].y))*1.0);
    if(dcmp(len-(double)p[u].r)<=0) return true;
    return false;
}

void init()
{
        memset(DFN,0,sizeof(DFN));
        memset(LOW,0,sizeof(LOW));
        memset(vis,0,sizeof(vis));
        memset(head,0,sizeof(head));
        top=tot=index=cnt=0;

        memset(belong,0,sizeof(belong));
        memset(out,0,sizeof(out));
        memset(in,0,sizeof(in));
}

int main()
{

    int ncase,T=0;
    scanf("%d",&ncase);
    while(ncase--)
    {

        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].c);
        }

        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==j) continue;
                if(dis(i,j)) add(i,j);
            }
        }


        for(int i=1;i<=n;i++)
            if(!DFN[i]) tarjan(i);

        for(int u=1;u<=n;u++) ///暴力枚举
        {
            for(int i=head[u];i;i=edge[i].nxt){
                int j=edge[i].v;

                if(belong[u]!=belong[j]){ ///
                    out[belong[u]]++;
                    in[belong[j]]++;
                }
            }
        }

        LL sum=0;

        for(int i=1;i<=cnt;i++)
        {
            if(!in[i]){ ///入度为0的强联通分量中找价值最小的
                int mins=INF;
                for(int j=1;j<=n;j++){
                    if(belong[j]==i){
                        if(mins>p[j].c) mins=p[j].c;
                    }
                }
                sum+=mins;
            }
        }
        printf("Case #%d: %lld\n",++T,sum);


    }
    return 0;
}




 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值