题目链接: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;
}