HDU 4606 Occupy Cities ★(线段相交+二分+Floyd+最小路径覆盖)

时间:2024-01-14 10:18:38

题意

有n个城市,m个边界线,p名士兵。现在士兵要按一定顺序攻占城市,但从一个城市到另一个城市的过程中不能穿过边界线。士兵有一个容量为K的背包装粮食,士兵到达一个城市可以选择攻占城市或者只是路过,如果攻占城市,就能装满背包。从城市到城市消耗的粮食等于两城市的距离,如果距离大于士兵当前的背包的容量,士兵就不能走这条路。士兵可以选择空降一次,空降不耗费。求p个士兵攻占所有城市所需要的最小背包容量k。

来源:2013 暑期多校联合训练 第一场

思路

非常好的一道比较综合的题目~首先我们能想到这是可相交的最小路径覆盖,当然还需处理一些问题:

距离。两个城市之间如果有边界线,我们还得绕着走,把边界线的两端点计算在内。为了方便,我们可以把所有城市和边界线的端点都当作图中的点,然后Floyd求最短路。当然,如果两个城市之间有边界线阻隔(判断线段相交),则这两个城市间不能直接连边。进一步(很重要),在计算包括所有边界线端点的图时,要判断的是如果任意两点的线段与某个边界线相交,则这两点都不能直接连边

点约束访问顺序。如果order[i] < order[j],则添加有向边(i, j)即可。

然后就是二分背包容量,根据最小路径覆盖是否小于士兵人数来更新答案。

代码

[cpp]
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, m) for (int i = begin; i < begin+m; i ++)
using namespace std;

const double oo = 1e50;
const double eps = 1e-8;
bool dy(double x,double y) { return x > y + eps;} // x > y
bool xy(double x,double y) { return x < y - eps;} // x < y
bool dyd(double x,double y) { return x > y - eps;} // x >= y
bool xyd(double x,double y) { return x < y + eps;} // x <= y
bool dd(double x,double y) { return fabs( x - y ) < eps;} // x == y

struct Point{
double x, y;
Point() {}
Point(double _x, double _y){
x = _x, y = _y;
}
Point(const Point &p){
x = p.x, y = p.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;
}
};
/** 两点间距离平方 **/
double dist2(Point a, Point b){
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}

struct Line{
Point s, e;
double k; //Slope
Line() {}
Line(Point _s, Point _e){
s = _s, e = _e;
k = atan2(e.y - s.y, e.x - s.x);
}
Line(double _sx, double _sy, double _ex, double _ey){
s = Point(_sx, _sy), e = Point(_ex, _ey);
k = atan2(_ey - _sy, _ex - _sx);
}
};
/** 线段相交,相交返回true **/
bool intersection(Line l1,Line l2){
return (max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&
max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&
max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&
max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&
((l2.s-l1.s)*(l1.e-l1.s))*((l2.e-l1.s)*(l1.e-l1.s)) < 0 &&
((l1.s-l2.s)*(l2.e-l2.s))*((l1.e-l2.s)*(l2.e-l2.s)) < 0);
}

const int MAXV = 205; //|V1|+|V2|
struct MaximumMatchingOfBipartiteGraph{
int vn;
vector <int> adj[MAXV];
void init(int n){ //二分图两点集点的个数
vn = n;
for (int i = 0; i <= vn; i ++) adj[i].clear();
}
void add_uedge(int u, int v){
adj[u].push_back(v);
adj[v].push_back(u);
}
bool vis[MAXV];
int mat[MAXV]; //记录已匹配点的对应点
bool cross_path(int u){
for (int i = 0; i < (int)adj[u].size(); i ++){
int v = adj[u][i];
if (!vis[v]){
vis[v] = true;
if (mat[v] == 0 || cross_path(mat[v])){
mat[v] = u;
mat[u] = v;
return true;
}
}
}
return false;
}
int hungary(){
MEM(mat, 0);
int match_num = 0;
for (int i = 1; i <= vn; i ++){
MEM(vis, 0);
if (!mat[i] && cross_path(i)){
match_num ++;
}
}
return match_num;
}
void print_edge(){
for (int i = 1; i <= vn; i ++){
for (int j = 0; j < (int)adj[i].size(); j ++){
printf("u = %d v = %d\n", i, adj[i][j]);
}
}
}
}match;

Line barrier[102];
Point city[102];
int order[102];
Point po[302];
double map[302][302];

void build(int n, int m){
REP(i, 1, n+2*m)
REP(j, 1, n+2*m)
map[i][j] = oo;
REP(i, 1, n+2*m)
REP(j, 1, n+2*m){
bool ok = true;
REP(k, 1, m){
Line tmp1 = Line(po[i], po[j]);
if (intersection(tmp1, barrier[k])){
ok = false;
break;
}
}
if (ok){
map[i][j] = sqrt(dist2(po[i], po[j]));
}
}
//floyd
REP(k, 1, n+2*m) REP(i, 1, n+2*m) REP(j, 1, n+2*m){
map[i][j] = min(map[i][j], map[i][k] + map[k][j]);
}
}

int main(){
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
int t;
scanf("%d", &t);
while(t --){
int n, m, p;
scanf("%d %d %d", &n, &m, &p);
REP(i, 1, n){
int x, y;
scanf("%d %d", &x, &y);
city[i] = Point(x, y);
po[i] = city[i];
}
REP(i, 1, m){
int x1, y1, x2, y2;
scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
barrier[i] = Line(x1, y1, x2, y2);
po[i+n] = barrier[i].s;
po[i+n+m] = barrier[i].e;
}
REP(i, 1, n){
int tmp;
scanf("%d", &tmp);
order[tmp] = i;
}
build(n, m);
// REP(i, 1, n+2*m)
// REP(j, 1, n+2*m){
// printf("i = %d j = %d dis = %f\n", i, j, map[i][j]);
// }
double l = 0.0, r = 1000010.0;
while(xy(l, r)){
double mid = MID(l, r);
match.init(2*n);
REP(i, 1, n)
REP(j, 1, n){
if (order[i] < order[j] && xyd(map[i][j], mid))
match.add_uedge(i, j+n);
}
if (n - match.hungary() > p){
l = mid;
}
else{
r = mid;
}
}
printf("%.2f\n", l);
}
return 0;
}
[/cpp]