LibreOJ#2548:「JSOI2018」绝地反击 – 题解

主要思路

这道题乍一看不是很能做,因为摆放状态很多。然而,计算机科学这种东西解决不了问题的时候不讲究正解,只讲究近似。所以,我们可以枚举一个旋转角 \(\theta\) 来决定最终摆放的形态。枚举之后就可以直接二分费用并建边做二分图。

代码

// LOJ2548.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 660, MAX_E = 1e5 + 200, INF = 0x3f3f3f3f;
const double pi = acos(-1.0);

int n, head[MAX_N << 1], current, start_pos, end_pos;
double xi[MAX_N], yi[MAX_N], R, tx[MAX_N], ty[MAX_N], dep[MAX_N << 1], S[MAX_N * MAX_N];

struct edge
{
    int to, nxt, weight;
} edges[MAX_E << 1];

void addpath(int src, int dst, int weight)
{
    edges[current].to = dst, edges[current].weight = weight;
    edges[current].nxt = head[src], head[src] = current++;
}

void addtube(int src, int dst, int weight) { addpath(src, dst, weight), addpath(dst, src, 0); }

double pow2(double x) { return x * x; }

double getDist(int i, int j) { return sqrt(pow2(xi[i] - tx[j]) + pow2(yi[i] - ty[j])); }

bool bfs()
{
    memset(dep, 0, sizeof(dep));
    queue<int> q;
    dep[start_pos] = 1, q.push(start_pos);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != -1; i = edges[i].nxt)
            if (dep[edges[i].to] == 0 && edges[i].weight > 0)
                dep[edges[i].to] = dep[u] + 1, q.push(edges[i].to);
    }
    return dep[end_pos] != 0;
}

int dfs(int u, int flow)
{
    if (u == end_pos || flow == 0)
        return flow;
    int ret = 0;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
    {
        if (edges[i].weight > 0 && dep[edges[i].to] == dep[u] + 1)
        {
            int di = dfs(edges[i].to, min(flow, edges[i].weight));
            ret += di, flow -= di;
            edges[i].weight -= di, edges[i ^ 1].weight += di;
        }
        if (flow == 0)
            break;
    }
    if (ret == 0)
        dep[u] = 0;
    return ret;
}

int dinic()
{
    int ret = 0;
    while (bfs())
        ret += dfs(start_pos, 2e9);
    return ret;
}

bool check(double mid)
{
    memset(head, -1, sizeof(head)), current = 0;
    start_pos = 0, end_pos = (n << 1) | 1;
    for (int i = 1; i <= n; i++)
        addtube(start_pos, i, 1);
    for (int i = 1; i <= n; i++)
        addtube(i + n, end_pos, 1);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (getDist(i, j) <= mid + 1e-10)
                addtube(i, j + n, 1);
    return dinic() == n;
}

double calc(double theta)
{
    for (int i = 1; i <= n; i++)
    {
        tx[i] = R * cos(theta + (i - 1) * (2 * pi / n));
        ty[i] = R * sin(theta + (i - 1) * (2 * pi / n));
    }
    int top = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            S[++top] = getDist(i, j);
    sort(S + 1, S + top + 1), top = unique(S + 1, S + top + 1) - S - 1;
    int l = 1, r = top;
    while (l < r)
    {
        int mid = (l + r) >> 1;
        if (check(S[mid]))
            r = mid;
        else
            l = mid + 1;
    }
    return S[l];
}

int main()
{
    scanf("%d%lf", &n, &R);
    for (int i = 1; i <= n; i++)
        scanf("%lf%lf", &xi[i], &yi[i]);
    double theta = 0, thetaf = calc(theta), ans = thetaf;
    for (double T = pi / n, curt; T > 1e-9; T *= 0.65)
    {
        curt = calc(theta + T), ans = min(ans, curt);
        if (curt < thetaf)
            theta += T, thetaf = curt;
        else
        {
            curt = calc(theta - T), ans = min(ans, curt);
            if (curt < thetaf)
                theta -= T, thetaf = curt;
        }
    }
    printf("%.6lf\n", ans);
    return 0;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *