HDU 5934 Bomb(强连通缩点)

Bomb

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 725    Accepted Submission(s): 260


Problem Description
There are N bombs needing exploding.

Each bomb has three attributes: exploding radius ri , position (xi,yi) and lighting-cost ci which means you need to pay ci cost making it explode.

If a un-lighting bomb is in or on the border the exploding area of another exploding one, the un-lighting bomb also will explode.

Now you know the attributes of all bombs, please use the minimum cost to explode all bombs.
 

Input
First line contains an integer T , which indicates the number of test cases.

Every test case begins with an integers N , which indicates the numbers of bombs.

In the following N lines, the ith line contains four intergers xi , yi , ri and ci , indicating the coordinate of ith bomb is (xi,yi) , exploding radius is ri and lighting-cost is ci .

Limits
- 1T20
- 1N1000
- 108xi,yi,ri108
- 1ci104
 

Output
For every test case, you should output 'Case #x: y', where x indicates the case number and counts from 1 and y is the minimum cost.
 

Sample Input
  
  
1 5 0 0 1 5 1 1 1 6 0 1 1 7 3 0 2 10 5 0 1 4
 

Sample Output
  
  
Case #1: 15
 

Source
 

Recommend
liuyiding   |   We have carefully selected several similar problems for you:   6010  6009  6008  6007  6006 
 



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

题目大意:
给你N个炸弹,对应已知其坐标和爆炸范围,以及引爆这个炸弹需要的花费,
对应如果引爆了炸弹a,没有引爆炸弹b,但是b炸弹在a炸弹的作用范围之内,
那么b炸弹也会被引爆,问将所有炸弹都引爆需要的最小花费。

首先O(n^2)预处理哪些炸弹可以被哪些炸弹引爆,得到一个有向图。
强连通缩点
缩点时顺便找出当前这个连通集合里的所需花费最小的那个炸弹
*/
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <cmath>
#include <string>
#include <sstream>
#include <map>
#include <set>
#define pi acos(-1.0)
#define LL long long
#define ULL unsigned long long
#define inf 0x3f3f3f3f
#define INF 1e18
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef pair<int, int> P;
const double eps = 1e-10;
const int maxn = 1e6 + 5;
const int N = 1e3 + 5;
const int mod = 1e8;

int stack[N], instack[N];
int belong[N];
int low[N], dfn[N];
int in[N], out[N];
int cnt, top, index, n;
vector<int> G[N];
LL waste[N], cas = 1;

struct Point{
    LL x, y, r, cost;
}p[N];

void init()
{
	memset(low, 0, sizeof(low));
	memset(dfn, 0, sizeof(dfn));
	memset(in, 0, sizeof(in));
	memset(out, 0, sizeof(out));
	memset(instack, 0, sizeof(instack));
	cnt = top = index = 0;
	for(int i = 1; i <= n; i++)
		G[i].clear();
}
void Tarjan(int u)
{
	stack[++top] = u;
	low[u] = dfn[u] = ++index;
	instack[u] = 1;
	for (int i = 0; i < G[u].size(); i++){
		int v = G[u][i];
		if (!dfn[v]){
			Tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if (instack[v])
			low[u] = min(low[u], dfn[v]);
	}
	if (dfn[u] == low[u]){
		++cnt;
		LL cost = INF;
		while (1){
			int v = stack[top--];
			instack[v] = 0;
			belong[v] = cnt;
			cost = min(cost, p[v].cost); // 找出这个连通集合里的所需花费最小的炸弹
			if (v == u) break;
		}
		waste[cnt] = cost;
	}
}
void solve()
{
	int i, j, v;
	for (i = 1; i <= n; i++){
		for (j = 0; j < G[i].size(); j++){
			v = G[i][j];
			if (belong[i] != belong[v]){  // 两点之间有边,但不是属于一个强联通分量的边
				out[belong[i]]++;	// 缩点后的点出度+1
				in[belong[v]]++;	// 缩点后的点入度+1
			}
		}
	}
	if (cnt == 1){
		printf("Case #%I64d: %I64d\n", cas++, waste[cnt]);
	}
	else {
		LL sum = 0;
		for (i = 1; i <= cnt; i++){
            if (in[i] == 0) // 入度为0 说明至少要点爆一个这个集合里的炸弹
                sum += waste[i]; // 点那个之前找出来的花费最小的
		}
        printf("Case #%I64d: %I64d\n", cas++, sum);
	}
}


LL dist(int i, int j)
{
    LL x = p[i].x - p[j].x;
    LL y = p[i].y - p[j].y;
    return (x*x + y*y);
}

int main(void)
{
//	freopen("in.txt","r", stdin);
    int T;
    cin >> T;
    while (T--)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%I64d %I64d %I64d %I64d", &p[i].x, &p[i].y, &p[i].r, &p[i].cost);
        init();
        for (int i = 1; i <= n; i++){
            for (int j = 1; j <= n; j++){
                if (i == j) continue;
                if (dist(i, j) <= p[i].r*p[i].r)
                    G[i].push_back(j);
            }
        }
        for (int i = 1; i <= n; i++)
            if (!dfn[i])
                Tarjan(i);
        solve();
    }

	return 0;
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值