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
- 1≤T≤20
- 1≤N≤1000
-
-
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
题目大意:
给定N个炸弹,每个炸弹有一个坐标 (x,y),一个爆炸半径 r 和一个人工引爆花费 c,如果一个炸弹的爆炸范围内有另外的炸弹,那么如果该炸弹爆炸,就会引爆所有爆炸范围内的炸弹(非人工引爆),求让所有炸弹爆炸的最小人工引爆花费。
分析:
首先,按照题意很明显可以建一张有向图,即对于每个炸弹,在该炸弹和其爆炸范围内的所有炸弹之间连有向边。
然后,需要找出所有的强连通分量。因为同一个强连通分量内的所有点(之后将炸弹抽象为点)均可相互到达,所以只需引爆其中的任一个,都可以引爆整个强连通分量。求强连通分量可以用Tarjan,同时在搜索过程中可以找到每个强连通分量内的人工引爆最小花费。
之后,将每个强连通分量缩成一个点,则可以得到一张有向无环图。可以想到,在这张图中所有入度为零的点(相应有强连通分量)必须人工引爆。所以,所有入度为零的点(强连通分量)中的最小花费之和就是答案。
具体解释见代码。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1005;
struct bomb{
ll x,y,r;
int c;
};
bomb b[maxn];
vector<int> gra[maxn];//邻接表
int low[maxn],dfn[maxn],sta[maxn],insta[maxn],belong[maxn],mincost[maxn],din[maxn];
int index,num,tp;
bool check(bomb a,bomb b){
if((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)<=a.r*a.r) return true;
else return false;
}
void tarjan(int x){//Tarjan算法
dfn[x]=low[x]=++num;
sta[++tp]=x;
insta[x]=1;
int len=gra[x].size();
rep(i,0,len-1){
int y=gra[x][i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(insta[y]==1) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
++index;
int cost=INF;
while(1){
int y=sta[tp--];
insta[y]=0;
belong[y]=index;//每个点所属于的强连通分量的序号
cost=min(cost,b[y].c);//在强连通分量内找最小花费
if(y==x) break;
}
mincost[index]=cost;//保存每个强连通分量的最小花费
}
}
int main(){
int t;
scanf("%d",&t);
rep(cas,1,t){
int n;
scanf("%d",&n);
rep(i,1,n){
scanf("%lld%lld%lld%d",&b[i].x,&b[i].y,&b[i].r,&b[i].c);
}
rep(i,0,maxn-1){
gra[i].clear();
}
//建有向图
rep(i,1,n){
rep(j,1,n){
if(i!=j){
if(check(b[i],b[j])){
gra[i].push_back(j); }
}
}
}
//初始化
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(sta,0,sizeof(sta));
memset(insta,0,sizeof(insta));
memset(belong,0,sizeof(belong));
memset(mincost,0,sizeof(mincost));
memset(din,0,sizeof(din));
index=num=tp=0;
rep(i,1,n){
if(!dfn[i]){
tarjan(i);
}
}
int i, j, v;
for (i = 1; i <= n; i++){
for (j = 0; j < gra[i].size(); j++){
v = gra[i][j];
if (belong[i] != belong[v]){ // 两点之间有边,但不是属于一个强联通分量的边
din[belong[v]]++; // 缩点后的点入度+1
}
}
}
ll sum=0;
rep(i,1,index){
if(din[i]==0){
sum+=(ll)mincost[i];
}
}
printf("Case #%d: %lld\n",cas,sum);
}
return 0;
}