P1378 油滴扩展

思路不难,但是实现过程还是错了。。。


给这道题的几个点补充一下:

  • 可以按照顺序滴下这些油滴。

  • 滴油滴的过程,是以滴下的点为圆心,向四周扩展出最大的半径。图形一定是个圆。

  • 油滴碰到边界就停止流。

因为用圆表示,所以你绝对不可能弄个数组模拟!我真的菜!

我最开始的时候还想着把坐标平移,结果没必要。。。

因为\(0 \leq N \leq 6\),所以弄个全排列是松松的。这道题就要用到全排列

滴入一个点的时候,我们就需要计算半径,并且记录下这个半径。前面的圆的半径会对后面的起限制作用。

那么问题来了:如何计算半径?

我们得到一个点的圆心后,就与上下左右横纵坐标限制一下。注意得到的是半径而不是直径。

然后就对前面的圆来做限制。

注意:我们要滴的点可能在先前的圆内或圆上或圆外。

当点在圆内,那么这个点的半径直接就为0,因为这些油滴不能相互溶解。

当点在圆上的时候半径还是0。

当点在圆外的时候,由数学必修2的知识,可以求出两个圆心的距离,然后减去先前圆的半径,得到的长度再与答案取min。

其他的就是基本操作了。

代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
const int maxn = 10;
const double pi = 3.1415926535897932384626434;
int n;
double x[maxn], y[maxn];
double minx, miny, maxx, maxy;
double ans;
bool vis[maxn];
double r[maxn];
double solve(int z)
{
    double temp1 = std::min(fabs(x[z] - minx), fabs(maxx - x[z]));
    double temp2 = std::min(fabs(y[z] - miny), fabs(maxy - y[z]));
    double res = std::min(temp1, temp2);
    for(int i = 1; i <= n; i++)
    {
        if(i != z && vis[i])
        {
            double d = sqrt((x[z] - x[i]) * (x[z] - x[i]) + (y[z] - y[i]) * (y[z] - y[i]));
            res = std::min(res, std::max(d - r[i], 0.0));
        }
    }
    return res;
}
void dfs(int t, double res)
{
    if(t == n + 1)
    {
        ans = std::max(ans, res);
        return;
    }
    for(int i = 1; i <= n; i++)
    {
        if(!vis[i])
        {
            vis[i] = true;
            r[i] = solve(i);
            dfs(t + 1, res + pi * r[i] * r[i]);
            r[i] = 0;
            vis[i] = false;
        }
    }
}
int main()
{
    scanf("%d", &n);
    scanf("%lf%lf%lf%lf", &minx, &miny, &maxx, &maxy);
    if(minx > maxx) std::swap(minx, maxx);
    if(miny > maxy) std::swap(miny, maxy);
    for(int i = 1; i <= n; i++) scanf("%lf%lf", &x[i], &y[i]);
    dfs(1, 0.0);
    printf("%.0lf\n", (maxx - minx) * (maxy - miny) - ans);
    return 0;
}
posted @ 2018-08-12 00:58  Garen-Wang  阅读(120)  评论(0编辑  收藏  举报