Bomb
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 0 Accepted Submission(s): 0
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.
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
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
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个炸弹,对应已知其坐标和爆炸范围,以及引爆这个炸弹需要的花费,对应如果引爆了炸弹a,没有引爆炸弹b,但是b炸弹在a炸弹的作用范围之内,那么b炸弹也会被引爆,问将所有炸弹都引爆需要的最小花费。
思路:
1、经典的最小点基的模型。我们首先O(n^2)预处理哪些炸弹可以被哪些炸弹引爆,得到一个有向图。
2、如果图中有有向环的话,我们可以将这一个有向环看成一个点,因为环内任意一个炸弹都能引爆这个环内所有的炸弹,所以我们使用Tarjan/Kosaraju之类的强连通算法缩点染色,使得图变成一个DAG(有向无环)图。
3、如果当前图变成了一个DAG图,那么度为0的节点一定是需要引爆的炸弹,因为这个节点中的炸弹不可能通过其他炸弹来引爆,只能通过直接引爆来达到引爆的目的,所以我们都将问题锁定在度为0的关键节点上来讨论,也就是所谓的最小点基问题。然后我们再简单分析一下,如果我们将所有度为0的节点都引爆了,那么度不为0的节点也一定会跟着被引爆,所以那么我们此时只需要将度为0的节点中找到一个对应的最小花费即可。
4、综上所述,我们Tarjan强联通缩点染色之后,找到度为0的节点,并且在其中找到花费最小的炸弹,累加即可。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<math.h>
using namespace std;
struct node2
{
int from;
int to;
int next;
}e[1515*1515*2];
struct node
{
int x,y,r,c;
}a[1515];
int head[15151];
int n,cont,sig,cnt,tt;
int degree[3500];
int stack[3500];
int color[3500];
int vis[3500];
int low[3500];
int dfn[3500];
int need[3500];
int contz[3500];
void add(int from,int to)
{
e[cont].to=to;
e[cont].from=from;
e[cont].next=head[from];
head[from]=cont++;
}
void Tarjan(int u)
{
vis[u]=1;
low[u]=dfn[u]=cnt++;
stack[++tt]=u;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(vis[v]==0)Tarjan(v);
if(vis[v]==1)low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u])
{
sig++;
do
{
color[stack[tt]]=sig;
vis[stack[tt]]=-1;
}
while(stack[tt--]!=u);
}
}
void Slove()
{
cnt=1;
sig=0;
tt=-1;
memset(dfn,0,sizeof(dfn));
memset(color,0,sizeof(color));
memset(vis,0,sizeof(vis));
memset(stack,0,sizeof(stack));
memset(low,0,sizeof(low));
memset(degree,0,sizeof(degree));
memset(contz,0x3f3f3f3f,sizeof(contz));
for(int i=0;i<n;i++)
{
if(vis[i]==0)
{
Tarjan(i);
}
}
for(int i=0;i<cont;i++)
{
int u=e[i].from;
int v=e[i].to;
if(color[u]!=color[v])
{
degree[color[v]]++;
}
}
for(int i=0;i<n;i++)
{
if(degree[color[i]]==0)
{
contz[color[i]]=min(contz[color[i]],a[i].c);
}
}
int output=0;
for(int i=1;i<=sig;i++)
{
if(degree[i]==0)
{
output+=contz[i];
}
}
printf("%d\n",output);
}
int main()
{
int t;
int kase=0;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].r,&a[i].c);
}
cont=0;
memset(head,-1,sizeof(head));
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
long long int tmp=(a[i].x-a[j].x);
long long int tmp2=(a[i].y-a[j].y);
long long int dis1=tmp*tmp+tmp2*tmp2;
long long int dis2=a[i].r;
dis2*=dis2;
if(dis1<=dis2)
{
add(i,j);
}
}
}
printf("Case #%d: ",++kase);
Slove();
}
}