油滴扩展

P1378 油滴扩展

题目描述

在一个长方形框子里,最多有 $N$ 个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界。必须等一个油滴扩展完毕才能放置下一个油滴。那么应该按照怎样的顺序在这 $N$ 个点上放置油滴,才能使放置完毕后所有油滴占据的总面积最大呢?(不同的油滴不会相互融合)

注:圆的面积公式 $S = \pi r^2$,其中 $r$ 为圆的半径。

输入格式

第一行,一个整数 $N$。

第二行,四个整数 $x, y, x', y'$,表示长方形边框一个顶点及其对角顶点的坐标。

接下来 $N$ 行,第 $i$ 行两个整数 $x_i, y_i$,表示盒子内第 $i$ 个点的坐标。

输出格式

一行,一个整数,长方形盒子剩余的最小空间(结果四舍五入输出)。

输入输出样例 #1

输入 #1

1
2
3
4
5
2
20 0 10 10
13 3
17 7

输出 #1

1
2
50

说明/提示

对于 $100%$ 的数据,$1 \le N \le 6$,坐标范围在 $[-1000, 1000]$ 内。

题解

本题意思是:在一个地方依次放n个点,他们扩散成圆,彼此不重合也不超过边界

我们可以用DFS来枚举每个点的放置顺序,计算每个点的扩散半径,最后求出剩余空间的最小值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <bits/stdc++.h>
using namespace std;
const double PI = acos(-1);
typedef long long ll;

int main(){
ios::sync_with_stdio(false);
cin.tie(0);
ll n;
ll x, y, x1, y1;
cin >> n >> x >> y >> x1 >> y1;
double L = min(x, x1), R = max(x, x1), U = max(y, y1), D = min(y, y1);
double ans = 0;
vector<pair<ll, ll>> loc(n);
for(auto &[xx, yy] : loc) cin >> xx >> yy;
vector<double> r(n);
vector<bool> vis(n);

// 计算第i个点的扩散半径
function<double(ll)> get_r = [&](ll i) {
double ansr = min({loc[i].first - L, R - loc[i].first, loc[i].second - D, U - loc[i].second});
for (ll j = 0; j < n; j++) {
if (vis[j]) {
ansr = min(ansr, (double)sqrt(pow(loc[i].first - loc[j].first, 2) + pow(loc[i].second - loc[j].second, 2)) - r[j]);
}
}
ansr = max(ansr, 0.0);
return ansr;
};

// DFS枚举每个点的放置顺序
function<void(ll, double)> dfs = [&](ll k, double sum){
if (k == n){
ans = max(ans, sum);
return;
}
for(ll i = 0; i < n; i++){
if (!vis[i]){
r[i] = get_r(i);
vis[i] = true;
dfs(k + 1, sum + r[i] * r[i] * PI);
vis[i] = false;
}
}
};
dfs(0, 0);
cout << (ll)round((R - L) * (U - D) - ans) << endl;
return 0;
}