三点连通。
给你一个平面,上面有很多个点,给你每个点的坐标,每个点还有个半径,两个点之间有一条无向边当且仅当以两点为圆心的两圆相切或相交。在给定的三个关键点之间互相连通的基础上,问你最多拿走多少个点(拿走的这些点相当于不存在了,完全不参与那三个关键点的连通)。
毫无疑问,这题首先我们得建边,比较两圆心的距离和半径之和即可。然后这个无向图就出来了。然后考虑一下,这题问的是最多拿走多少点,那相当于求最少多少个点参与(包括这三个点) 能使这三个点连通。
这个问题可以转化为找一个点,这个点到三个关键点的最小途径点数的和最小。(不要问我怎么想出来的)
怎么求一个点到另一个点的最小途径点数?很巧妙,转化为最小途径边数+1,而最小途径边数又可以转化为求解边权都是1
的最短路。为什么能这么转化?因为找到的最短路肯定是简单路,不会有环这种东西。之后,因为是无向图,以这三个关键点分别为源点求最短路就好了。
(将 边数 / 点数 最大 / 最小 转化为边权、转化为最短路问题的思想)
下面我来说一点自己的思考。
这个做法只适用于三个点以内,四个点就不行了。设想四个点在一条链上(当然,四个点也可以有别的构型),那么上述枚举的最小值无论如何都会重复计算边数,得到的一定是错误的答案。但是,三个点不会有这种情况。三个点的两种构型(链、奔驰车标型)都可以找到一个中介点,从中介点到三个点的路径都不重叠。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
const int INF = 1e9;
const int MAX = 200 + 5;
int N, T;
int ans;
int g[MAX][MAX];
int d[4][MAX];
bool inq[MAX];
struct Point
{
int x, y, r;
};
Point p[MAX];
void init()
{
ans = -1;
}
double dist(Point p1, Point p2)
{
return sqrt(pow((double)(p1.x - p2.x), 2.0) + pow((double)(p1.y - p2.y), 2.0));
} // 需要输出double,所以为了稳妥,让参数都变成double
void spfa(int s)
{
queue<int> q;
memset(inq, 0, sizeof inq);
fill(d[s] + 1, d[s] + N + 1, INF);
q.push(s);
inq[s] = true;
d[s][s] = 0;
for (; !q.empty();)
{
int x = q.front();
q.pop();
inq[x] = false;
for (int i = 1; i <= N; i++)
{
if (g[x][i] != INF && d[s][x] + g[x][i] < d[s][i])
{
d[s][i] = d[s][x] + g[x][i];
if (!inq[i])
{
q.push(i);
inq[i] = true;
}
}
}
}
}
int main()
{
scanf("%d", &T);
for (; T--;)
{
scanf("%d", &N);
init();
for (int i = 1; i <= N; i++)
scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].r);
for (int i = 1; i <= N; i++)
{
g[i][i] = 0;
for (int j = i + 1; j <= N; j++)
{
if (dist(p[i], p[j]) > (double)(p[i].r + p[j].r)) // 判断两圆是否相切或相交
g[i][j] = g[j][i] = INF;
else g[i][j] = g[j][i] = 1; // 将点最少转化为边最少,从而可以最短路求解
}
}
spfa(1);
spfa(2);
spfa(3);
if (d[1][2] == INF || d[1][3] == INF || d[2][3] == INF)
{
printf("-1\n");
continue;
}
for (int i = 1; i <= N; i++)
{
if (d[1][i] != INF && d[2][i] != INF && d[3][i] != INF) // 没这句就WA(好几个INF会溢出的),可见注意细节的重要性
ans = max(ans, N - (d[1][i] + d[2][i] + d[3][i] + 1)); // 别忘了还有个1
}
printf("%d\n", ans);
}
return 0;
}