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

题目:https://vjudge.net/contest/338569#problem/P

显然可以想到引爆花费最小需要选择能引爆尽量多的,如果A能引爆B,B能引爆C,一定是引爆A最优。

如果有一些点可以相互引爆,那么就引爆这些点里花费最小的。

“能互相引爆”这显然是一个强连通分量,因此用tarjan求强连通分量之后,对于每个拓扑序最开头(入度为0)的连通分量,选择花费最小的进行引爆,即可求出答案。

ac代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e3 + 5;

int _, n, kase;

struct Point {
    ll x, y, r;
    int c;
} p[maxn];

vector<int> G[maxn];

ll getDis(const Point &a, const Point &b) {
    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}

int dfn[maxn], low[maxn], scc[maxn], tot, scc_cnt;
stack<int> st;

void tarjan(int x) {
    dfn[x] = low[x] = ++tot;
    st.push(x);
    for (auto v:G[x]) {
        if (!dfn[v]) {
            tarjan(v);
            low[x] = min(low[x], low[v]);
        } else if (!scc[v]) {
            low[x] = min(low[x], dfn[v]);
        }
    }
    if (dfn[x] == low[x]) {
        ++scc_cnt;
        int v;
        while (v != x) {
            v = st.top();
            st.pop();
            scc[v] = scc_cnt;
        }
    }
}

void findScc() {
    tot = scc_cnt = 0;
    memset(dfn, 0, sizeof(dfn));
    memset(scc, 0, sizeof(scc));
    for (int i = 1; i <= n; ++i) {
        if (!dfn[i]) {
            tarjan(i);
        }
    }
}

int deg[maxn], cost[maxn];

int main() {
    scanf("%d", &_);
    while (_--) {
        memset(deg, 0, sizeof(deg));
        memset(cost, 0x3f, sizeof(deg));
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) {
            scanf("%lld%lld%lld%d", &p[i].x, &p[i].y, &p[i].r, &p[i].c);
            p[i].r = p[i].r * p[i].r;
        }
        for (int i = 1; i <= n; ++i) {
            G[i].clear();
            for (int j = 1; j < i; ++j) {
                if (getDis(p[i], p[j]) <= p[i].r) {
                    G[i].push_back(j);
                }
                if (getDis(p[j], p[i]) <= p[j].r) {
                    G[j].push_back(i);
                }
            }
        }

        findScc();

        for (int i = 1; i <= n; ++i) {
            for (auto v:G[i]) {
                if (scc[v] != scc[i]) {
                    ++deg[scc[v]];
                }
            }
            cost[scc[i]] = min(cost[scc[i]], p[i].c);
        }

        int ans = 0;
        for (int i = 1; i <= scc_cnt; ++i) {
            if (!deg[i]) {
                ans += cost[i];
            }
        }
        printf("Case #%d: %d\n", ++kase, ans);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值