Tarjan 缩点——HDU 5934

  • 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5934

  • 题意:给出N个炸弹的坐标爆炸半径还有引爆的花费,求爆炸所有炸弹所需要的最小花费。

  • 分析:我们发现如果炸弹A在B的爆炸范围内,同时炸弹B也在炸弹A的爆炸范围内,那么无论引爆哪一个,都会导致它们俩都爆炸,所以我们可以把A和B当做一个炸弹,花费为最小花费即可。同理,可以延伸到 n 个炸弹,如果引爆这 n 个炸弹中的任意一个,最终都会导致这 n 个炸弹都爆炸,那么也可以把这 n 个炸弹都用一个来替代,并用其中的最小花费来表示。这一步,我们可以用Tarjan缩点来完成。
    建边:如果炸弹A能引爆炸弹B,那么建立一条有向边 AB 。那么在求联通分量退栈的时候,找出最小花费,标记给这个连通块的。然后结束后,我们枚举每一条边,如果它们不在一个联通块里,我们就给指向的那个联通块增加一个入度。最终的花费就是所有入度为0的联通块(必须要引爆的炸弹)的花费和了。

  • AC代码:

/*************************************************************************
    > File Name: B.cpp
    > Author: Akira
    > Mail: qaq.febr2.qaq@gmail.com
    > Created Time: 2017年05月05日 星期五 19时37分48秒
 ************************************************************************/

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<cmath>
#include<vector>
#include<set>
#include<list>
#include<ctime>
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))

using namespace std;

#define MaxN 1010
#define MaxM MaxN*1000
#define INF 0x3f3f3f3f
#define PI 3.1415926535897932384626
const int mod = 1e9+7;
const int eps = 1e-8;
#define bug cout << 88888888 << endl;

struct Node
{
    int x,y,r,c;
}node[MaxN];

int T,N;

struct Edge
{
    int u,v,next;
}edge[MaxM],e[MaxM];
int cont, head[MaxN];
void add(int u, int v)
{
    edge[cont].u = u;
    edge[cont].v = v;
    edge[cont].next = head[u];
    head[u] = cont++;
}

int low[MaxN], dfn[MaxN];
int Stack[MaxN], top;
int belong[MaxN];
int vis[MaxN];
int Index, cnt;
int cost[MaxN];
void init()
{
    cont = 0;
    MST(head,-1);

    MST(cost,INF);
    MST(dfn, -1);
    CLR(low);
    CLR(vis);
    Index = cnt = 1;
    top = 0;
}
void Tarjan(int x)
{
    low[x] = dfn[x] = Index;
    Index++;
    Stack[++top] = x;
    vis[x] = true;
    for(int i=head[x];i!=-1;i=edge[i].next)
    {
        int v = edge[i].v;
        if(dfn[v] == -1)
        {
            Tarjan(v);
            low[x] = min(low[x], low[v]);
        }
        else if(vis[v])
        {
            low[x] = min(low[x], dfn[v]);
        }
    }
    if( low[x] == dfn[x] )
    {
        for(int v = -1;v!=x;top--)
        {
            v = Stack[top];
            vis[v] = false;
            belong[v] = cnt;
            cost[cnt] = min(cost[cnt], node[v].c);
        }
        cnt++;
    }
}
int in[MaxN];

void solve(int t)
{
    init();
    for(int i=1;i<=N;i++)
    {
        int xi = node[i].x;
        int yi = node[i].y;
        int ri = node[i].r;
        for(int j=1;j<=N;j++)
        {
            if(i==j) continue;
            LL dis = Sqr((LL)(node[j].x-xi)) + Sqr((LL)(node[j].y-yi));
            if( dis<=Sqr((LL)ri) ) 
            {
                add(i,j);
                //cout << i << " " << j << endl;
            }
        }
    } 
    for(int i=1;i<=N;i++) 
        if(dfn[i]==-1)
            Tarjan(i);

    CLR(in);
    for(int i=0;i<cont;i++)
    {
        int u = edge[i].u;
        int v = edge[i].v;
        if(belong[u] != belong[v])
        {
            //cout << u << ": " << belong[u] << endl;
            //cout << v << ": " << belong[v] << endl; 
            in[belong[v]]++;
        }
    }
    int ans = 0;
    for(int i=1;i<cnt;i++)
    {
        //cout << in[i] << endl;
        if(in[i]==0) ans += cost[i];
    }
    printf("Case #%d: %d\n",  t, ans);

}

int main()
{
    scanf("%d", &T);
    for(int t=1;t<=T;t++)
    {
        scanf("%d", &N);
        for(int i=1;i<=N;i++) scanf("%d%d%d%d", &node[i].x, &node[i].y, &node[i].r, &node[i].c);
        solve(t);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值