HDU 4606 Occupy Cities (计算几何+Floyd+二分+最小路径覆盖)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4606

 

题意:给你N个城市的坐标,现在有P个士兵要去占领这N个城市,但是路上有m个路障,都是线段,士兵不能越过路障前进。每个士兵都有一个容量相同的干粮袋,每走一个单位长度消耗一单位干粮,每当到一个城市时干粮就能补满,给出N个城市的攻占顺序,问最少的干粮容量,使得这P个士兵能在遵守攻占顺序的前提下占领N个城市

 

思路:最终所转化的模型是最小路径覆盖,按照攻占顺序建立DAG,含义是让P个士兵走出P条路径覆盖所有点,故而二分答案,如果该答案下的最小路径覆盖数 <= p 即是可行解。故而可以先将所有点之间的最短路找出来,对于每个障碍物,把它的两个端点也加入到距离的处理中,如果对于某两点的线段与m条障碍线中的任意一条相交,那么便不能直接求两者间距(注意判断线段相交时端点不用处理),然后Floyd处理出最短距离,最后按照所给的顺序(需拓扑序,从前到后)进行连边,最后跑二分图匹配进行判断是否可行

 

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <utility>
#include <functional>
#include <string>
#include <cctype>
#include <set>
#include <map>

#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;

const int maxn = 400;
const int inf = 0x3f3f3f3f;
const double eps = 1e-7;

int sgn(double x)
{
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    return 1;
}

struct point
{
    double x, y;
    point(double x = 0, double y = 0) : x(x), y(y) {}

    point operator +(const point &b)const
    {
        return point(x - b.x, y - b.y);
    }

    point operator -(const point &b)const
    {
        return point(x - b.x, y - b.y);
    }

    double operator ^(const point &b)const      //叉积
    {
        return x * b.y - y * b.x;
    }

    double operator *(const point &b)const      //点积
    {
        return x * b.x + y * b.y;
    }

    void input()
    {
        scanf("%lf%lf", &x, &y);
    }
};

struct line
{
    point s, e;
    line() {}
    line(point _s, point _e)
    {
        s = _s, e = _e;
    }

    void input()
    {
        s.input();
        e.input();
    }
};

double dist(point a, point b)
{
    return sqrt((a - b) * (a - b));
}

bool inter(line l1, line l2)
{
    return sgn((l2.s - l1.e) ^ (l1.s - l1.e)) * sgn((l2.e-l1.e) ^ (l1.s - l1.e)) < 0 &&
           sgn((l1.s - l2.e) ^ (l2.s - l2.e)) * sgn((l1.e-l2.e) ^ (l2.s - l2.e)) < 0;
}

int n, m, P, cnt;
int sc[maxn];

point p[maxn];
line l[maxn];
double dis[maxn][maxn];

int uN, vN;
int g[maxn][maxn];
int linker[maxn];
bool used[maxn];

bool dfs(int u)
{
    for (int v = 0; v < vN; v++)
        if (g[u][v] && !used[v])
        {
            used[v] = true;
            if (linker[v] == -1 || dfs(linker[v]))
            {
                linker[v] = u;
                return true;
            }
        }
    return false;
}

int hungary()
{
    int res = 0;
    memset(linker, -1, sizeof(linker));
    for (int u = 0; u < uN; u++)
    {
        memset(used, false, sizeof(used));
        if (dfs(u))
            res++;
    }
    return res;
}

void floyd()
{
    for (int k = 0; k < cnt; k++)
        for (int i = 0; i < cnt; i++)
            for (int j = 0; j < cnt; j++)
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}

bool ok(double mid)
{
    uN = vN = n;
    memset(g, 0, sizeof(g));
    for (int i = 0; i < n; i++)
        for (int j = i + 1; j < n; j++)
        {
            if (sgn(dis[sc[i]][sc[j]] - mid) <= 0)
                g[sc[i]][sc[j]] = 1;
        }

    int res = n - hungary();
    return res <= P ? true : false;
}

void solve()
{
    double l = 0, r = 1e5;
    while (sgn(r - l))
    {
        double mid = (l + r) / 2;
        if (ok(mid))
            r = mid;
        else
            l = mid;
    }
    printf("%.2f\n", l);
}

int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d%d%d", &n, &m, &P);
        cnt = 0;
        for (int i = 0; i < n; i++)
            p[cnt++].input();
        for (int i = 0; i < m; i++)
        {
            l[i].input();
            p[cnt++] = l[i].s;
            p[cnt++] = l[i].e;
        }
        for (int i = 0; i < n; i++)
        {
            scanf("%d", &sc[i]);
            sc[i]--;
        }
        for (int i = 0; i < cnt; i++)
        {
            for (int j = 0; j < cnt; j++)
            {
                if (i == j)
                {
                    dis[i][j] = 0;
                    continue;
                }
                bool flag = false;
                for (int k = 0; k < m; k++)
                {
                    line li = line(p[i], p[j]);
                    if (inter(l[k], li))
                    {
                        flag = true;
                        break;
                    }
                }
                if (flag)
                    dis[i][j] = inf;
                else
                    dis[i][j] = dist(p[i], p[j]);
            }
        }
        floyd();
        solve();
    }
    return 0;
}

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值