【HDU 5934】Bomb

博客围绕n个炸弹引爆最少费用问题展开。每个炸弹有位置、爆炸半径和引爆费用,引爆炸弹时半径内炸弹会被连锁引爆。通过建图将能相互引爆的炸弹连有向边,用Tarjan缩点,以强连通分量中最小费用为新点费用,统计入度为0的强连通分量得出最少费用。

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

1.题目链接。题目大意:n个炸弹,每个炸弹具有四个参数,分别是这个炸弹的位置(x,y),爆炸时的半径,以及引爆它的费用。引爆炸弹的规则是,当一个炸弹被引爆,在他爆炸半径内的炸弹也会被引爆。问引爆这n个炸弹需要的最少费用。

2.如果一个炸弹能够引爆另外一个炸弹,我们连一条指向被引爆的炸弹的有向边。这样n^2建图之后,显然是一个有向图。在这个有向图中,所有的强连通分量其实可以被看做一个点,因为只要引爆其中任意一个这个强连通分量中的所有炸弹都将被引爆,所以Tarjan缩点,缩点之后这个点的费用是这个强连通分量里费用最小的那个。然后,怎么统计答案?其实很简单,只需要统计有多少个入度为0的强连通分量即可,因为入读为零,没有人来引爆它,只能花费费用引爆。所以这个题完事。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define met(a,b) memset(a,b,sizeof a)
typedef long long ll;
using namespace std;
const int N = 1005;
const int M = 24005;
int vis[N], dfn[N], low[N], head[N], stack1[N], num[N], in[N];
ll cost[N];
int n, m, tot, son, maxn, tim, top, cut;
ll ans;
struct EDG { int to, next; }edg[N * N];
struct node { ll x, y, r, c; }a[N];
bool cmp(node f, node g) { return f.c < g.c; }
void add(int u, int v) {
	edg[tot].to = v; edg[tot].next = head[u]; head[u] = tot++;
}
void init() {
	met(head, -1);
	tot = tim = top = cut = 0;
	met(vis, 0);
	met(edg, 0);
	met(in, 0);
	met(cost, inf);
	met(stack1, 0); met(num, 0); met(dfn, 0); met(low, 0);
}
void Tarjan(int u) {
	int v;
	low[u] = dfn[u] = ++tim;
	stack1[top++] = u;
	vis[u] = 1;
	for (int e = head[u]; e != -1; e = edg[e].next) {
		v = edg[e].to;
		if (!dfn[v]) {
			Tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if (vis[v]) {
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (low[u] == dfn[u]) 
	{
		cut++;
		do {
			v = stack1[--top];
			num[v] = cut;
			cost[cut] = min(cost[cut], a[v].c);
			vis[v] = 0;
		} while (u != v);
	}
}
double dis(node a, node b)
{
	return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}
int main()
{
	int T;
	scanf("%d", &T);
	for (int ca = 1; ca <= T; ca++)
	{
		init();
		int n;
		scanf("%d", &n);
		for (int i = 1; i <= n; i++)
		{
			scanf("%lld%lld%lld%lld", &a[i].x, &a[i].y, &a[i].r, &a[i].c);
		}
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				if (dis(a[i], a[j]) <= a[i].r)add(i, j);
			}
		}

		for (int i = 1; i <= n; i++)if (!dfn[i])Tarjan(i);
		for (int i = 1; i <= n; i++)
		{
			for (int j = head[i]; j!=-1; j = edg[j].next)
			{
				int v = edg[j].to;
				if (num[i] != num[v])in[num[v]]++;

			}
		}
		ll ans = 0;
		for (int i = 1; i <= cut; i++)
		{
			if (!in[i])ans += cost[i];
		}
		printf("Case #%d: %lld\n", ca, ans);

	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值