题意:二维平面上有 n 个炸弹,每个炸弹有个引爆的代价和爆炸半径,问至少花费多少代价才能引爆所有炸弹。炸弹爆炸的时候会引爆半径及内所有炸弹。
解:构造,将一个炸弹引爆另一条边的过程变成一条有向边。炸弹就是点。所以,那些入度为0的点就是必须要引爆的点(无炸弹将其引爆)。由此构图,强连通分量缩点,使得整张图无连通,求出所有入度为0的权值即可(一个强连通分量可以与一个点等价,其权值为内部最小的权值)。
代码:
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1005;
const int maxm = maxn*maxn;
const int inf =999999;
struct node{
int v,u,next;
}edge[ maxm ];
int head[ maxn ],cnt;
int vis[ maxn ],low[ maxn ],dfn[ maxn ],id;
int n,m,ans1;
int cost[ maxn ];
int belong[ maxn ],inde[ maxn ];//缩点,入度
int X[maxn],Y[maxn],R[maxn];
stackq;
void init(){
cnt=0;
id=0;
ans1=0;
memset( vis,0,sizeof( vis ));
memset( dfn,-1,sizeof(dfn) );
memset( low,-1,sizeof( low ));
memset( head,-1,sizeof( head ));
}
void addedge( int a,int b ){
edge[ cnt ].v=a;
edge[ cnt ].u=b;
edge[ cnt ].next=head[ a ];
head[ a ]=cnt++;
}
void tarjan( int now ){
dfn[ now ]=low[ now ]=id++;
vis[ now ]=1;
q.push( now );
for( int i=head[ now ];i!=-1;i=edge[ i ].next ){
int next=edge[ i ].u;
if( dfn[ next ]==-1 ){
tarjan( next );
low[ now ]=min( low[ now ],low[ next ]);
}
else if( vis[ next ]==1 ){
low[ now ]=min( low[ now ],dfn[ next ] );
}
}
if( low[ now ]==dfn[ now ] ){
ans1++;
while( 1 ){
int tmp;
tmp=q.top(),q.pop();
vis[ tmp ]=0;
belong[ tmp ]=ans1;
if( tmp==now ) break;
}
}
}
int main(){
int t,T=0;
scanf("%d",&t);
while(t--)
{
init();
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d",&X[i],&Y[i],&R[i],&cost[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j) continue;
long long now=1LL*(X[j]-X[i])*(X[j]-X[i])+1LL*(Y[j]-Y[i])*(Y[j]-Y[i]);
if(now<=1LL*R[i]*R[i])
addedge(i,j);
}
}
while( !q.empty() ) q.pop();
for( int i=1;i<=n;i++ ){
if( dfn[ i ]==-1 ){
tarjan( i );
}
}
int a,b;
memset( inde,0,sizeof( inde ));
for( int i=0;i