HDU 4606 Occupy Cities ★(线段相交+二分+Floyd+最小路径覆盖)
题意
有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]举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG