原题链接
题意:一个炸弹有一个爆破范围,如果一个炸弹爆破,其范围内的所有炸弹也会爆炸,被引爆的炸弹也是如此,但是引爆炸弹需要消耗一定的代价,现在给出以些炸弹的位置和爆炸半径,问引爆所有炸弹需要的最小代价。
题解:做题先是想到了dp,后来又想到了并查集,后来发现都解决不了A可以引爆B但是,B不一定引爆A的问题。后来想到了强连通分量。建立一个有向图,他可以解决前面的引爆,后面的必然引爆,我们选择的时候可以只选择最高的节点,但是最高的节点不一定只有一个,这就用到了强连通分量,我们可以只选择头节点连通分量里面权值最小的节点。而且这里要注意的是只需要选择缩点后入度为0的连通分量即可,但是在训练赛的时候代码写的有些问题,将id记录成了bool类型的还有其他各种各样的细节问题,下面是训练赛时的代码:(是错的)
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
#define int long long
const int N = 1010;
const int M = 2e6 + 10;
int h[N], e[M], ne[M], idx, top;
bool id[N];//这里错了,应该是int
int timestamp;
int dfn[N], low[N], scc_cnt;
int Size[N];
int stk[N], w[N], n;
int iout[N];
int ans = 0;
struct node
{
int x, y;
int r, w;
} a[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++timestamp;
stk[++top] = u, in_stk[u] = true;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!dfn[j])
{
tarjan(j);
low[u] = min(low[j], low[u]);
}
else if (in_stk[u])//这里错了,应该是j
low[u] = min(low[u], dfn[j]);
}
if (dfn[u] == low[u])
{
++scc_cnt;
int y;
do
{
y = stk[top--];
in_stk[y] = false;
id[y] = scc_cnt;
cout<<id[y]<<"--"<<scc_cnt<<"--"<<y<<endl;
Size[scc_cnt]++;
w[scc_cnt] = min(w[scc_cnt], a[y].w); // 强连通分量的值
} while (y != u);
}
}
signed main()
{
int t;
scanf("%lld",&t);
while (t--)
{
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
h[i] = -1;
for (int i = 1; i <= n; i++)
w[i] = 0x3f3f3f3f3f;
for (int i = 1; i <= n; i++)
in_stk[i] = false;
for (int i = 1; i <= n; i++)
iout[i] = 0;
//这里忘记初始化low 和dfn了
ans = 0;
top = 0;
idx = 0;
scc_cnt = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i].x >> a[i].y >> a[i].r >> a[i].w;
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if(i==j) continue;
int xx = (a[i].x - a[j].x) * (a[i].x - a[j].x);
int yy = (a[i].y - a[j].y) * (a[i].y - a[j].y);
if (xx + yy <= a[i].r * a[i].r) add(i, j);//printf("%lld %lld->\n",i,j);
}
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i]) tarjan(i);
}
printf("%lld--%lld\n",id[4],id[5]);
for (int i = 1; i <= n; i++)
{
for (int j = h[i]; ~j; j = ne[j])
{
int k = e[j];
int x = id[k];
int y = id[i];
if (x != y) iout[x]++;
printf("%lld-->%lld",x,y);
}
}
for (int i = 1; i <= scc_cnt; i++)
{
if (iout[i] == 0) ans += w[i];
}
printf("%lld\n", ans);
}
return 0;
}
下面是AC代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
#define int long long
const int N = 1010;
const int M = 2e6 + 10;
int h[N], e[M], ne[M], idx, top;
int id[N];
int timestamp;
int dfn[N], low[N], scc_cnt;
int Size[N];
int stk[N], w[N], n;
bool in_stk[N];
int iout[N];
int ans = 0;
struct node
{
int x, y;
int r, w;
} a[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++timestamp;
stk[++top] = u, in_stk[u] = true;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!dfn[j])
{
tarjan(j);
low[u] = min(low[j], low[u]);
}
else if (in_stk[j])
low[u] = min(low[u], dfn[j]);
}
if (dfn[u] == low[u])
{
++scc_cnt;
int y;
do
{
y = stk[top--];
in_stk[y] = false;
id[y] = scc_cnt;
//cout<<id[y]<<"--"<<scc_cnt<<"--"<<y<<endl;
Size[scc_cnt]++;
w[scc_cnt] = min(w[scc_cnt], a[y].w); // 强连通分量的值
} while (y != u);
}
}
signed main()
{
int t;
scanf("%lld",&t);
int cou=0;
while (t--)
{
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
h[i] = -1;
for (int i = 1; i <= n; i++)
w[i] = 0x3f3f3f3f3f;
for (int i = 1; i <= n; i++)
in_stk[i] = false;
for (int i = 1; i <= n; i++)
iout[i] = 0;
for(int i=1;i<=n;i++) dfn[i]=0;
for(int i=1;i<=n;i++) low[i]=0;
ans = 0;
top = 0;
idx = 0;
scc_cnt = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i].x >> a[i].y >> a[i].r >> a[i].w;
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if(i==j) continue;
int xx = (a[i].x - a[j].x) * (a[i].x - a[j].x);
int yy = (a[i].y - a[j].y) * (a[i].y - a[j].y);
if (xx + yy <= a[i].r * a[i].r) add(i, j);//printf("%lld %lld->\n",i,j);
}
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i]) tarjan(i);
}
//printf("%lld--%lld\n",id[4],id[5]);
for (int i = 1; i <= n; i++)
{
for (int j = h[i]; ~j; j = ne[j])
{
int k = e[j];
int x = id[k];
int y = id[i];
if (x != y) iout[x]++;
//printf("%lld-->%lld",x,y);
}
}
for (int i = 1; i <= scc_cnt; i++)
{
if (iout[i] == 0) ans += w[i];
}
printf("Case #%lld: %lld\n",++cou, ans);
}
return 0;
}
总结:在这个题中,总结到了就是即使是图形类的问题也不一定都是用几何去做,在做题的时候应该是发散思维,多想想如何去建图,怎么把问题巧妙的转化,毕竟将问题转化为图上解决比在二维平面中解决要简单。还有一个就是我们应该记住强连通分量缩点之后可以将一个图转化为有向无环图,这样解决问题的时候也就变得简单了。