题目描述
小明和朋友们一起去郊外植树,他们带了一些在自己实验室精心研究出的小树苗。
小明和朋友们一共有 n 个人,他们经过精心挑选,在一块空地上每个人挑选了一个适合植树的位置,总共 n 个。他们准备把自己带的树苗都植下去。
然而,他们遇到了一个困难:有的树苗比较大,而有的位置挨太近,导致两棵树植下去后会撞在一起。
他们将树看成一个圆,圆心在他们找的位置上。如果两棵树对应的圆相交,这两棵树就不适合同时植下(相切不受影响),称为两棵树冲突。
小明和朋友们决定先合计合计,只将其中的一部分树植下去,保证没有互相冲突的树。他们同时希望这些树所能覆盖的面积和(圆面积和)最大。
输入格式:
输入的第一行包含一个整数 n ,表示人数,即准备植树的位置数。
接下来 n 行,每行三个整数 x, y, r,表示一棵树在空地上的横、纵坐标和半径。
输出格式:
输出一行包含一个整数,表示在不冲突下可以植树的面积和。由于每棵树的面积都是圆周率的整数倍,请输出答案除以圆周率后的值(应当是一个整数)。
样例输入
6
1 1 2
1 4 2
1 7 2
4 1 2
4 4 2
4 7 2
样例输出
12
评测用例规模与约定:
对于 30% 的评测用例,1 <= n <= 10;
对于 60% 的评测用例,1 <= n <= 20;
对于所有评测用例,1 <= n <= 30,0 <= x, y <= 1000,1 <= r <= 1000。
思路
题目可以理解为:平面内分布着不同大小的圆,在圆与圆出现重合的情况下选择部分圆,使得被选的圆们总面积尽可能大。
为什么不考虑贪心或者dp? 圆与圆之间相互重叠,且圆的半径有大有小,一个圆的选用与否可能会影响到其他数个圆,被影响的圆又将影响到跟它们有关的圆,并且可能会出现多个圆重叠、圆之间包含的情况。没法找到最优子结构或者贪心的方式。
换一个思路,一个圆有选和不选两种可能性,而圆的数量最多只有30个,可以尝试通过回溯找到所有可能。此时复杂度达到 2 ^ n(n <= 30) (过大),但我们不需要回溯所有的情况,可以有以下枝剪方案:
1.如果一个圆不与其他任何圆重叠,它一定会被选择。(n - 1)
2.如果一个圆与前面任意被选中的圆重叠,该圆一定不会被选用。 (n - 1)
一个圆无论是否与其他圆重叠,它的分支在回溯中都可能被剪去,复杂度最坏情况为 2 ^ (n / 2) ,可以接受。
实现
1.首先可以想到,不与任何其他圆重叠的圆一定会被选入,所以它不需要参与回溯。
2.提前对圆之间的重叠关系进行处理(O (n ^ 2) )并记录各圆面积,省去在回溯中进行计算。
3.回溯,通过比较得到最大值。
#include<bits/stdc++.h>
using namespace std;
int n, ans;
int cir[30][3]; //circles
int iso[30][30]; //isoverlapped
int are[30]; //area
int vis[30]; //visaul:0未选用,1选用,-1不考虑
//2.执行回溯
void dfs(int idx, int sum) { // i下标,sum面积和
//结束条件
if(idx == n - 1) {
ans = max(ans, sum);
return;
}
//回溯
if(vis[idx] == -1) dfs(idx + 1, sum); //当前圆孤立,不考虑
else {
vis[idx + 1] = 0; //不选用当前圆
dfs(idx + 1, sum);
int f = 1; //考虑选用当前圆
for(int i = 0; i < idx + 1; i++)
if(vis[i] && iso[i][idx + 1] == 1) {
f = 0;
break;
}
if(f) {
vis[idx + 1] = 1;
dfs(idx + 1, sum + are[idx + 1]);
}
}
}
int main() {
cin >> n;
for(int i = 0; i < n; i++)
cin >> cir[i][0] >> cir[i][1] >> cir[i][2];
//1.找出重叠的圆,将孤立的圆计入答案
for(int i = 0; i < n; i++) {
are[i] = cir[i][2] * cir[i][2];
int f = 1;
for(int j = 0; j < n; j++) {
int dx = cir[i][0] - cir[j][0];
int dy = cir[i][1] - cir[j][1];
int dr = cir[i][2] + cir[j][2];
if(dx * dx + dy * dy < dr * dr) {
iso[i][j] = 1;
f = 0;
}
}
if(f) {
ans += are[i];
vis[i] = -1;
}
}
dfs(-1, ans);
cout << ans;
}