几何和网络流的结合 战火星空

这题一开始折磨了我一个上午,下狠心重编,结果一下就过了。看来重编也算是个好方法。

题目大意:在平面上有N个boss,boss始不会动,还有M架小飞机,给出每架小飞机的飞行路线(是线段,飞到终点后消失),还有射程(即一个圆的半径),飞行速度,能量,每攻击boss一个单位的时间就要消耗一个单位的能量(攻击的时间可以是小数),boss不能在同一时刻被多架飞机攻击。求boss被攻击的最大总时间。

首先要知道如何求boss被每架飞机攻击的时间。这里需要求直线和圆的交点(或许会有两个,也可能没有)。

直线方程:ax + by + c = 0:

设飞行路线线段的两个端点的坐标分别为(x1, y1),(x2, y2)。

a = y1 - y2;

b = x2 - x1;

c = x1 * y2 - x2 * y1。

圆的方程 :

设boss的坐标为(p, q),射程为r

(x - p) ^ 2 + (y - q) ^ 2 = r ^ 2

一元二次方程的标准形式为 

a' x ^ 2 + b' x + c' = 0,运用公式法  x = ( - b' ± sqrt ( b' ^ 2 - 4 a' c') ) / (2 a')。

为了区分开直线方程的a和一元二次方程的a,一元二次方程的是a'(有一撇‘)。

a' = 1 + (a ^ 2) / (b ^ 2);

b' = - 2 p + 2 a c / (b ^ 2) + 2 a q / b;

c' = - (r ^ 2) + p ^ 2 + q ^ 2 + (c ^ 2) / (b ^ 2) + 2 q c / b。

当b为0时(即x1 = x2)要特别判断一下,这时候x就等于x1,y的值也好求了。

求出了交点,那么攻击的时间也容易求了,然后就是网络流构图。


对于“boss不能在同一时刻被多架飞机攻击”这个条件,正好能解决。

然后做一次小数的网络流就可以了。

#include <cstdio>
#include <cstring>
#include <cmath> 
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
using namespace std;

#define x first
#define y second
typedef pair<double, double> point;
typedef point interval;

const int N = 20, M = 20;
const int FV = 2 + M + 2 * N * M;
const int FE = (M + (M + 1) * 2 * N * M) * 2;

int n, m;
point boss[N], start[M], end[M];
int V[M], R[M], E[M];

void init()
{
	scanf("%d%d\n", &n, &m);
	for (int i = 0; i < n; i ++) 
		scanf("%lf%lf\n", &boss[i].x, &boss[i].y);
	for (int i = 0; i < m; i ++)
	{
		scanf("%lf%lf", &start[i].x, &start[i].y);
		scanf("%lf%lf", &end[i].x, &end[i].y);
		scanf("%d%d%d\n", &V[i], &R[i], &E[i]);
	}
}

int source = 0, sink = 1, vc = 2, ec = 0;
int from[FV], to[FE], next[FE];
double cap[FE];

void insert(int u, int v, double c)
{
	to[ec] = v;
	next[ec] = from[u];
	cap[ec] = c;
	from[u] = ec ++;
	
	to[ec] = u;
	next[ec] = from[v];
	cap[ec] = 0e+0;
	from[v] = ec ++;
}

int Arrc;
pair<double, int> Arr[2 * N * M];
int plane_cur[M];

set<int> plane_set;

inline double squ(double a) 
{
	return a * a;
}

inline double dis(point &a, point &b) 
{
	return sqrt(squ(a.x - b.x) + squ(a.y - b.y));
}

interval calc(point &a, point &b, int V, point p, int R)
{
	double linea = a.y - b.y, lineb = b.x - a.x,
		linec = a.x * b.y - a.y * b.x;
	
	point c, d;
	if (lineb != 0e+0) 
	{
		double equa = 1e+0 + squ(linea) / squ(lineb),
			equb = -2e+0 * p.x + 
				2e+0 * linea * linec / squ(lineb) + 
				2e+0 * linea * p.y / lineb,
			equc = - squ(R) + squ(p.x) + squ(p.y) + 
				squ(linec) / squ(lineb) + 
				2e+0 * p.y * linec / lineb;
		
		double t = squ(equb) - 4e+0 * equa * equc;
		if (t < 0e+0) return make_pair(0e+0, 0e+0);
		t = sqrt(t);
		
		c.x = (- equb - t) / 2e+0 / equa;
		d.x = (- equb + t) / 2e+0 / equa;
		c.y = (- linea * c.x - linec) / lineb;
		d.y = (- linea * d.x - linec) / lineb;
	}
	else 
	{
		c.x = d.x = a.x;
		double t = squ(R) - squ(a.x - p.x);
		if (t < 0e+0) return make_pair(0e+0, 0e+0);
		t = sqrt(t);
		c.y = t + p.y; 
		d.y = - t + p.y;
	}
	if ((a < b) != (c < d)) swap(c,d);
	if (a < b)
	{
		c = max(c, a);
		d = min(d, b);
		if (c > d) return make_pair(0e+0, 0e+0);
	}
	else 
	{
		c = min(c, a);
		d = max(d, b);
		if (d > c) return make_pair(0e+0, 0e+0);
	}
	return make_pair(dis(a, c) / V, dis(a, d) / V);
}

void graph()
{
	memset(from, -1, sizeof(from));
	for (int i = 0; i < m; i ++) 
	{
		insert(source, vc, E[i]);
		plane_cur[i] = vc ++;
	}
	for (int i = 0; i < n; i ++) 
	{
		Arrc = 0;
		for (int j = 0; j < m; j ++)
		{
			interval t = calc(start[j], end[j], V[j], boss[i], R[j]);
			if (t.first != t.second)
			{
				Arr[Arrc ++] = make_pair(t.first, j);
				Arr[Arrc ++] = make_pair(t.second, j);
			}
		}
		sort(Arr, Arr + Arrc);
		for (int i = 0; i < Arrc; i ++) 
		{
			set<int>::iterator iter = plane_set.find(Arr[i].second);
			if (iter == plane_set.end()) plane_set.insert(Arr[i].second);
			else plane_set.erase(iter);
			
			if (i < Arrc - 1) 
			{
				double timer = Arr[i + 1].first - Arr[i].first;
				for (iter = plane_set.begin(); 
					iter != plane_set.end(); iter ++)
					insert(plane_cur[*iter], vc, timer);
				insert(vc ++, sink, timer);
			}
		}
	}
}

queue<int> Q;
int lev[FV];

bool bfs() 
{
	while (! Q.empty()) Q.pop();
	memset(lev, -1, sizeof(lev));
	lev[source] = 0;
	Q.push(source);
	
	while (! Q.empty())
	{
		int u = Q.front();
		Q.pop();
		for (int e = from[u]; e != -1; e = next[e])
		{
			int v = to[e];
			if (cap[e] > 0e+0 && lev[v] == -1)
			{
				lev[v] = lev[u] + 1;
				if (v == sink) return true;
				Q.push(v);
			}
		}
	}
	return false;
}

double search(int u, double flow) 
{
	double rest = flow;
	if (u == sink) return flow;
	for (int e = from[u]; e != -1; e = next[e]) {
		int v = to[e];
		if (cap[e] > 0e+0 && lev[v] == lev[u] + 1)
		{
			double t = search(v, min(rest, cap[e])); 
			if (t > 0e+0)
			{
				cap[e] -= t;
				cap[e ^ 1] += t;
				rest -= t;
			}
		}	
	}
	return flow -= rest;
}

void dinic()
{
	double ans = 0e+0;
	
	while (bfs())
	{
		double t = search(source, 1e+4);
		if (t == 0e+0) break;
		ans += t;
	}
	
	printf("%.6lf\n", ans);
}

int main() 
{
	freopen("star.in", "r", stdin);
	freopen("star.out", "w", stdout);
	
	init();
	graph();
	dinic();
	
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值