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
- −108≤xi,yi,ri≤108
- 1≤ci≤104
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个炸弹,给出每个炸弹的坐标,爆炸半径,以及引爆所需要的花费,每个炸弹爆炸能连锁引爆在其爆炸半径内的所有炸弹爆炸,问若要引爆所有炸弹,如何引爆花费最小。
连通图求缩点模板题。对于每个炸弹,我们要使代价最小即引爆他不需要花费,间接的通过别的炸弹引爆,炸弹间的关系大概有以下几种情况:
1、两炸弹互不影响
2、两炸弹相互影响
3、一炸弹引爆可以连锁半径内另一炸弹,而另一炸弹不影响这个炸弹。也就是单向影响。
4、炸弹间的引爆可以传递,如两个炸弹本身距离很远,但一路上有其他炸弹作为索引会一路影响连锁导致爆炸,这种连锁可能是单向的也可能是双向的。甚至可能是多个炸弹多次影响。
排除这些复杂的情况,我们将炸弹的引爆关系看做是一个连通图。
双向影响的关系为无向边,单向影响为有向边。而对于前面提到的相互影响,即,在这些炸弹中,引爆任意一个都会使这一群炸弹连锁爆炸,我们这一群炸弹看做一个炸弹,该炸弹的引爆花费为这群炸弹中最小的那个。
而另一种情况,如一群炸弹又延伸出分支连锁个别单个炸弹,而这个连锁是单向的,我们无法看做缩点,那么就建立一个单向边。使其联通,对于这种情况我们应该如何引爆,很明显,若被引出的分支花费小,我们引爆了花费小的炸弹,但是花费大的炸弹仍需单独引爆,我们花费了多次。而不关心花费大小,直接根据连锁反应引爆最初的开始炸弹,这样仅需引爆一次,而且没有多余的花费,因此此处引爆的方法应该无视花费直接引爆源头。
对于一个点连锁一群点的情况亦是如此。
建好图后我们应该求的是要引爆的最少次数,也就是独立的连通图的个数,也就是这些图中入度为零的所有点。此处无视花费大小
对于刚才提到的相互影响的点,已经被计算为一个缩点,那么这个缩点的花费即一些炸弹中最小的,此处用到了花费的比较。因此缩点使用一群炸弹中花费最小的,入度为0的点无视花费直接引爆。
代码如下:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn=1008;
struct node
{
double x,y,r;
int cos;
} a[maxn];
int n,low[maxn],dfn[maxn],cnt,in,id[maxn],num,cost[maxn],inum[maxn];
bool maps[maxn][maxn],vis[maxn];
stack<int>q;
void tarjan(int u)
{
int i;
low[u]=dfn[u]=++cnt;
q.push(u);
vis[u]=true;
for(int i=1; i<=n; i++)
{
if(!maps[u][i])continue;
if(!dfn[i])
{
tarjan(i);
low[u]=min(low[u],low[i]);
}
else if(vis[i])
{
low[u]=min(low[u],dfn[i]);
}
}
if(low[u]==dfn[u])
{
num++;
while(!q.empty())
{
int tmp=q.top();
q.pop();
id[tmp]=num;
if(cost[id[tmp]]>a[tmp].cos)cost[id[tmp]]=a[tmp].cos;
vis[tmp]=0;
if(tmp==u)break;
}
}
}
double dis(double x1,double y1,double x2,double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main()
{
int t,cas=1;
scanf("%d",&t);
while(t--)
{
memset(vis,false,sizeof(vis));
memset(maps,false,sizeof(maps));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(inum,0,sizeof(inum));
memset(id,0,sizeof(id));
memset(cost,0x3f,sizeof(cost));
while(!q.empty())q.pop();
cnt=in=num=0;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%lf%lf%lf%d",&a[i].x,&a[i].y,&a[i].r,&a[i].cos);
for(int j=1; j<i; j++)
{
double tmp=dis(a[i].x,a[i].y,a[j].x,a[j].y);
if(a[i].r>=tmp) maps[i][j]=true;
if(a[j].r>=tmp) maps[j][i]=true;
}
}
for(int i=1; i<=n; i++)
if(!dfn[i])tarjan(i);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
if(maps[i][j]&&id[i]!=id[j]) inum[id[j]]++;
int ans=0;
for(int i=1;i<=num;i++)
if(!inum[i])ans+=cost[i];
printf("Case #%d: %d
",cas++,ans);
}
}