题目链接:添加链接描述
找强连通块,块内选择引爆任意一个都能使整个块引爆,我们只需要把强连通块缩成一个点,然后取入度为0的点引爆,每个点取cost最小即可。
代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#include <map>
using namespace std;
typedef long long ll;
typedef double db;
const int mod=1e9+7;
const int maxn=1e3+11;
const double eps=0.00000001;
int t,n,color[maxn],ins[maxn],stack[maxn],dfn[maxn],low[maxn],deg[maxn];
int cnt,num,tot,top;
vector<int>G[maxn],scc[maxn];
struct Point{
int x,y,r,cost;
}p[maxn];
ll cal(int i,int j){
return 1ll*(p[i].x-p[j].x)*(p[i].x-p[j].x)+1ll*(p[i].y-p[j].y)*(p[i].y-p[j].y);
}
void tarjan(int x){
dfn[x]=low[x]=++num;
stack[++top]=x,ins[x]=1;
for(int i=0;i<G[x].size();++i){
int g=G[x][i];
if(!dfn[g]){
tarjan(g);
low[x]=min(low[x],low[g]);
}
else if(ins[g]) low[x]=min(low[x],dfn[g]);
}
if(dfn[x]==low[x]){
++cnt;
int y;
do{
y=stack[top--],ins[y]=0;
color[y]=cnt,scc[cnt].push_back(y);
}while(x!=y);
}
}
int main(){
int x,y,Case=0;
cin>>t;
while(t--){
tot=top=cnt=num=0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(ins,0,sizeof(ins));
memset(deg,0,sizeof(deg));
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d%d%d%d",&p[i].x,&p[i].y,&p[i].r,&p[i].cost);
}
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j){
if(i==j) continue;
ll dis=cal(i,j);
if(dis<=1ll*p[i].r*p[i].r) G[i].push_back(j);
if(dis<=1ll*p[j].r*p[j].r) G[j].push_back(i);
}
}
for(int i=1;i<=n;++i){
if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=cnt;++i){
for(int j=0;j<scc[i].size();++j){
int g=scc[i][j];
for(int k=1;k<=n;++k){
if(color[k]==color[g]) continue;
ll dis=cal(g,k);
if(dis<=1ll*p[k].r*p[k].r){
//cout<<1ll*p[k].r*p[k].r<<" "<<dis<<endl;
deg[i]=1;
break;
}
}
if(deg[i]) break;
}
}
ll ans=0;
for(int i=1;i<=cnt;++i){
//cout<<deg[i]<<endl;
if(!deg[i]){
ll Min=1e18;
for(int j=0;j<scc[i].size();++j){
Min=min(Min,1ll*p[scc[i][j]].cost);
}
ans+=Min;
}
scc[i].clear();
}
for(int i=1;i<=n;++i) G[i].clear();
printf("Case #%d: %lld\n",++Case,ans);
}
}