大致题意:
给出n个地雷,每颗地雷有一个爆炸范围,这颗地雷爆炸后,这个范围内的地雷都会炸。求最少引爆多少地雷能使得所有的地雷都爆炸。
大致思路:
把每个地雷看作点,每颗地雷都向其能引爆的地雷连边。建图完成后用Tarjan缩点,求出出度为0的强连通分量的个数就是答案。
#include<iostream> #include<cstdio> #include <algorithm> #include<cmath> #include<cstring> using namespace std; const int inf=1<<30; const int nMax=3015; const int mMax=5001000; class edge{ public: int v,nex; };edge e[mMax]; int k,head[nMax];//head[i]是以点i为起点的链表头部 void addedge(int a,int b){//向图中加边的算法,注意加上的是有向边//b为a的后续节点既是a---->b e[k].v=b; e[k].nex=head[a]; head[a]=k;k++; } int dfn[nMax],low[nMax],sta[nMax],top,atype,belon[nMax],dep; //atype 强连通分量的个数 bool insta[nMax]; void Tarjan(int u){ //我的Tarjan模版 int i,j; dfn[u]=low[u]=++dep; sta[++top]=u; insta[u]=1; for(i=head[u];i;i=e[i].nex){ int v=e[i].v; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); } else{ if(insta[v])low[u]=min(low[u],dfn[v]); } } if(dfn[u]==low[u]){ atype++; //强连通分量个数 do{ j=sta[top--]; belon[j]=atype; //第j个点属于第type个连通块 insta[j]=0; }while(u!=j); } } int out[nMax]; //每个连通块的出度 int in[nMax]; void init(){ k=1; dep=1; top=atype=0; memset(insta,0,sizeof(insta)); //是否在栈中 memset(head,0,sizeof(head)); //静态链表头指针 memset(low,0,sizeof(low)); //Tarjan的low数组 memset(dfn,0,sizeof(dfn)); //Tarjan的dfn数组 memset(out,0,sizeof(out)); //记录每个强连通分量的出度 memset(in,0,sizeof(in)); //记录每个强连通分量的入度 memset(belon,0,sizeof(belon)); //记录每个点属于哪一个强连通分量 } int mine[nMax][3]; int main(){ int i,j,m,n,a,b,cas,c; scanf("%d",&cas); while(cas--) { init(); cin>>n; for(i=0;i<n;i++) { cin>>mine[i][0]>>mine[i][1]>>mine[i][2]; } for(i=0;i<n;i++) { for(j=0;j<n;j++) { if(i==j)continue; // double len=sqrt((double)mine[i][2]); int len=mine[i][2]/2; if(abs(mine[i][0]-mine[j][0])<=len&&abs(mine[i][1]-mine[j][1])<=len) { addedge(i,j); // cout<<i<<" add "<<j<<endl; } } } for(i=0;i<n;i++) { if(!dfn[i])Tarjan(i); } for(i=0;i<n;i++) { for(j=head[i];j;j=e[j].nex) { a=belon[i]; b=belon[e[j].v]; if(a!=b) { out[a]++; in[b]++; } } } int ans=0; for(i=1;i<=atype;i++) { if(in[i]==0) { ans++; } } cout<<ans<<endl; } return 0; }