题目链接
题意:给n个炸弹的坐标和爆炸半径,引爆代价。当另一个炸弹在一个炸弹的爆炸范围之内,则这个炸弹也会爆炸。求让所有炸弹爆炸的最小花费。
思路:根据炸弹之间的关系建图,比如a能引爆b就在a到b建立一条有向边。然后将图缩点,然后统计入度为0的点的爆炸代价,累加就是结果。
同一个强联通分量里面的点取价值最小的那个引爆,入度不为0的点肯定能由其他入度为0的点到达。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
using namespace std;
const int maxn=1e6+10;
int n;
struct zp
{
LL x,y,r;
int c;
} meg[maxn];
struct edge
{
int u,v,next;
} node[maxn*10];
LL get_dis(zp a,zp b)
{
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
int head[maxn],cont,sk[maxn],insk[maxn],belong[maxn],top,tot,tim,low[maxn],dfn[maxn];
/*
dfn[i]代表dfs到i点的时间戳是多少
low[i]代表i节点或者在i节点后面访问到的节点的最小时间戳是多少
belong[i]代表将图缩点之后i点对应的节点标号
sk[i]代表栈
insk[i]代表i节点是否在栈中
*/
void init()
{
memset(head,-1,sizeof(head));
tim=tot=top=cont=0;
memset(dfn,0,sizeof(dfn));
memset(insk,0,sizeof(insk));
}
void add(int u,int v)
{
node[cont].u=u,node[cont].v=v;
node[cont].next=head[u];
head[u]=cont++;
}
void tarjan(int u)//tarjan缩点
{
dfn[u]=low[u]=++tim;//第一次访问到节点u
sk[top++]=u,insk[u]=1;//入栈
for(int i=head[u]; i+1; i=node[i].next)
{
int v=node[i].v;
if(!dfn[v]) tarjan(v);//v节点没有被访问过 1
if(insk[v]) low[u]=min(low[u],low[v]);//v在栈中 2
/*
2的if有两种执行条件:
1.当1的if执行时也就是dfn[v]==0,执行完后如果v在栈中的话更新low[u],如果v不在栈中的话
说明v是图的一个割点,又因为v是在u之后遍历的,所以low[v]是大于dfn[u]的所以没有必要更新。
2.当1的if不执行时,就是dfn[v]!=0说明v节点已经被访问过了,如果v还在栈中,说明v是在u之前
被访问的,更新一下low[u],如果v不在栈中就说明v和u不在同一个强联通分量里面,所以不用更新
*/
}
if(low[u]==dfn[u])//u不一定是割点,上面2的情况的后面的那种有可能u就不是割点,u只有v一个出点,并且和u不在同一个强联通块中
{
tot++;
int tmp;
do
{
tmp=sk[--top];
belong[tmp]=tot;
insk[tmp]=0;
}
while(u!=tmp);//在栈中u之前的点属于同一个块
}
}
int deg[maxn];
void solve()
{
memset(deg,0,sizeof(deg));
for(int i=0; i<n; i++)
{
for(int j=head[i]; j+1; j=node[j].next)
{
int v=node[j].v;
if(belong[i]==belong[v]) continue;//属于同一个块的不计算
deg[belong[v]]++;//计算点的入度
}
}
int ans=0;
for(int i=1; i<=tot; i++)
{
if(!deg[i])//点的入度为0是则这个点必须被引爆,入度不为0的带你肯定能能被其他点引爆
{
int tmp=10010;
for(int j=0; j<n; j++)
{
if(belong[j]==i)//同一个强联通块中的点找价值最小的引爆
{
tmp=min(tmp,meg[j].c);
}
}
ans+=tmp;//累计答案
}
}
printf("%d\n",ans);
}
int main()
{
int ncase;
scanf("%d",&ncase);
for(int ca=1; ca<=ncase; ca++)
{
init();
scanf("%d",&n);
for(int i=0; i<n; i++)
scanf("%lld%lld%lld%d",&meg[i].x,&meg[i].y,&meg[i].r,&meg[i].c);
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
if(i==j) continue;
if(get_dis(meg[i],meg[j])<=meg[i].r*meg[i].r)
add(i,j);
}
}
for(int i=0; i<n; i++)
if(!dfn[i])
tarjan(i);
printf("Case #%d: ",ca);
solve();
}
}