HDU-5934 Bomb(强连通分量缩点)

题意:

N个炸弹都有一个坐标一个爆炸半径以及一个引爆花费,如果一个炸弹在另一个炸弹的爆炸范围内,则引爆另一个炸弹,可以导致当前炸弹也爆炸,如果当前炸弹的爆炸范围有其它未炸的炸弹,那么也能导致这些炸弹爆炸,以此联动。

思路:

如果i炸弹的爆炸范围内有j炸弹,则建有向边i->j,然后缩一下点,每个强连通分量内只要引爆一个,其它都会炸,所以把最小的花费给这个强连通分量,最后我们只需要引爆所有入度为0的强连通分量,且花费最小。

代码:

#include <bits/stdc++.h>  
#define ll long long
using namespace std;  
const int inf = 0x3f3f3f3f;    
const ll mod = 1e9+7;    
const ll bas = 1001;  
const int maxn = 1005;    
const int maxm = 1000005;    
struct node {int u, v, next;} edge[maxm];  
int no, head[maxn];    
int _index, low[maxn], dfn[maxn], S[maxn], top, vis[maxn];    
int deg[maxn];    
int bcc[maxn], cnt;  
queue<int> q;  
int n, m;  
/*unordered_*/set<ll> _hash;
inline void init()    
{
    no = 0; _hash.clear();  
    memset(head, -1, sizeof head);    
    _index = top = 0; cnt = 0;    
    memset(dfn, 0, sizeof dfn);    
    memset(bcc, 0, sizeof bcc);    
    memset(deg, 0, sizeof deg); 
}    
inline void add(int u, int v)    
{    
    edge[no].u = u, edge[no].v = v;    
    edge[no].next = head[u];    
    head[u] = no++;    
}    
void tarjan(int u)    
{    
    dfn[u] = low[u] = ++_index;    
    S[++top] = u;    
    vis[u] = 1;    
    for(int k = head[u]; k != -1; k = edge[k].next)    
    {    
        int v = edge[k].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])    
    {    
        ++cnt;    
        do    
        {    
            bcc[S[top]] = cnt;    
            vis[S[top--]] = 0;    
        }    
        while(S[top+1] != u);    
    }    
}  
struct point
{
	int x, y, c, r;
} aw[maxn];  
int val[maxn];
ll dis2(point p1, point p2)
{
	return 1ll*(p1.x-p2.x)*(p1.x-p2.x)+1ll*(p1.y-p2.y)*(p1.y-p2.y);
}
int main()    
{    
	//freopen("in.txt", "r", stdin); 
    int t, u, v; 
    scanf("%d", &t);
    for(int _ = 1; _ <= t; ++_)    
    {    
        scanf("%d", &n); init();
        for(int i = 1; i <= n; ++i)
        scanf("%d %d %d %d", &aw[i].x, &aw[i].y, &aw[i].r, &aw[i].c);
        for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j)
        if(i != j)
        {
        	ll d = dis2(aw[i], aw[j]);
        	if(d <= 1ll*aw[i].r*aw[i].r) add(i,j);
        }
        for(int i = 1; i <= n; ++i)    
        if(!dfn[i]) tarjan(i);    
        for(int i = 0; i < no; ++i)    
        {    
            int u = edge[i].u, v = edge[i].v;    
            if(bcc[u] != bcc[v] && _hash.find(bas*bcc[u]+bcc[v]) == _hash.end())    
            {  
                _hash.insert(bas*bcc[u]+bcc[v]);  
                ++deg[bcc[v]];
            }
        }  
        memset(val, 0x3f, sizeof val);
        for(int i = 1; i <= n; ++i)
        val[bcc[i]] = min(val[bcc[i]], aw[i].c);
        ll ans = 0;
        for(int i = 1; i <= cnt; ++i)  
        if(deg[i] == 0) ans += val[i];
        printf("Case #%d: %lld\n", _, ans);
    }    
    return 0;    
}  


继续加油~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值