题目链接:hdu 5934
参考博客:https://www.cnblogs.com/fightfordream/p/6093256.html
题意:有N个炸弹,每个炸弹有一个坐标,一个爆炸范围和一个爆炸花费,如果一个炸弹的爆炸范围内有另外的炸弹,那么如果该炸弹爆炸,就会引爆所有爆炸范围内的炸弹,求让所有炸弹爆炸的最小花费。
思路:先n^2的把每个炸弹爆炸范围内的炸弹都连一条有向边,然后再找强连通分量缩点,这样会形成多个DAG,然后对于每个DAG找一个入度为0的点,找这个入度为0的点里面耗费最小的去引爆,就可以了。
在DAG图里,入度不为0的点一定可以由某个入度为0的点到达(由于无环,所以从任何入度不为0的 点往回走,必然终止于一个入度为0的点)
代码:
还要注意一点的是,建边要是选择的方法不同,可能会超时,见代码注释那里,原因还未知。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
const int maxn=1010;
struct point{
LL x,y,r,c; ///注意这里是long long 虽说数据不超过int,但在判断距离时可能会溢出
}p[maxn];
struct node{
int u,v,nxt;
}edge[maxn*maxn]; ///开平方倍
int DFN[maxn],LOW[maxn],vis[maxn],head[maxn];
int belong[maxn],top,tot,cnt,stacks[maxn],index;
int out[maxn],in[maxn]; ///每个强联通分量块的出度,入度
void add(int u,int v){///建立边
tot++;
edge[tot].u=u;
edge[tot].v=v;
edge[tot].nxt=head[u];
head[u]=tot;
}
///不要这样建边,会超时,原因未知
//void add(int x,int y)
//{
// tot++;
// nexts[tot]=point[x];
// point[x]=tot;
// v[tot]=y;
//}
void tarjan(int x) ///模板
{
stacks[++top]=x;
vis[x]=1;
DFN[x]=LOW[x]=++index;
for(int i=head[x];i;i=edge[i].nxt)
{
int j=edge[i].v;
if(!DFN[j]){
tarjan(j);
LOW[x]=min(LOW[x],LOW[j]);
}
else if(vis[j]) LOW[x]=min(LOW[x],LOW[j]);
}
if(DFN[x]==LOW[x])
{
cnt++;
int item;
do{
item=stacks[top--];
vis[item]=0;
belong[item]=cnt;
}while(item!=x);
}
}
int dcmp(double x)
{
if(fabs(x)<1e-8) return 0;
else return x>0?1:-1;
}
bool dis(int u,int v){ ///判断是否可以连边
double len=sqrt(((p[u].x-p[v].x)*(p[u].x-p[v].x)+(p[u].y-p[v].y)*(p[u].y-p[v].y))*1.0);
if(dcmp(len-(double)p[u].r)<=0) return true;
return false;
}
void init()
{
memset(DFN,0,sizeof(DFN));
memset(LOW,0,sizeof(LOW));
memset(vis,0,sizeof(vis));
memset(head,0,sizeof(head));
top=tot=index=cnt=0;
memset(belong,0,sizeof(belong));
memset(out,0,sizeof(out));
memset(in,0,sizeof(in));
}
int main()
{
int ncase,T=0;
scanf("%d",&ncase);
while(ncase--)
{
init();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld%lld",&p[i].x,&p[i].y,&p[i].r,&p[i].c);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) continue;
if(dis(i,j)) add(i,j);
}
}
for(int i=1;i<=n;i++)
if(!DFN[i]) tarjan(i);
for(int u=1;u<=n;u++) ///暴力枚举
{
for(int i=head[u];i;i=edge[i].nxt){
int j=edge[i].v;
if(belong[u]!=belong[j]){ ///
out[belong[u]]++;
in[belong[j]]++;
}
}
}
LL sum=0;
for(int i=1;i<=cnt;i++)
{
if(!in[i]){ ///入度为0的强联通分量中找价值最小的
int mins=INF;
for(int j=1;j<=n;j++){
if(belong[j]==i){
if(mins>p[j].c) mins=p[j].c;
}
}
sum+=mins;
}
}
printf("Case #%d: %lld\n",++T,sum);
}
return 0;
}