Package delivery is really a tiring work. I’m spending all day on the roads, crossing bridges and highways through the city, and visiting various people in different buildings. Anytime someone asks me if I’m busy, stressed or overworked and the answer will likely be an emphatic yes.
But I have my experience in my job. At the beginning of the day, I arrive at the company and receive the packages of today’s work. Before I set out to deliver them, I have to carefully consider which package should be sent first and which way I should go. It’s really a heavy headache for me.
The city consists of totally M two-way roads, each of which is a straight segment line or a circle. A straight one contains two endpoints that can be denoted as Ai(xiA,yiA) and Bi(xiB,yiB) on a two-dimensional map, while a circle-shaped road should be expressed as a centre point Oi(xi,yi) and its radius Ri. Roads may cross with others (even on the endpoint), but no two roads will overlap. The coordinates and radius are measured in centimetre (cm) and the scale of the map is 1: 100000. That is, two locations with actual distance of 1 kilometre (km) will be 1cm apart on the map. Further more, roads are not neccessarily connected in the city.
My company locates at (Cx,Cy) on the map. As I said before, I will occur there at the beginning of the day. When I set out from somewhere and leave for the next destination, I have two choices: on foot, or by taxi (don’t laugh at me anyway). My walking speed keeps at Vwalk km/h and it won’t change. However, car speed limit differs on every road due to the different traffic situations. Cars can only run along the roads rather than other areas, while walking is of no constraints.
If I choose to walk to the next destination, I go there directly along a straight line from start to target and will not stop on the way. But taking a taxi is a little bit complicated. Firstly I’ll choose a road, and walk to the nearest position from me on that road. If there’s a tie, all candidates are available. After waiting for a taxi for Twait minutes, I step on a taxi at that position and choose a road as my target. The car will run along the roads and send me to the target road, and I’ll get off the taxi at the nearest location to the destination on that road. Finally, I walk from where I get off the taxi along a straight way to the destination, and will not take another taxi any more (even it might be faster, but it costs money for me). Because of my urgent demands, the taxi always runs at limit speed on every specific road. But the taxi drivers are not well enough - I have to determine the way myself to prevent being ripped off.
N packages’ destinations spread all over the city. The ith package’s destination locates at Pi(xi,yi) which may occur at any corner of the city except on any roads. Besides, there’s an urgency degree for each package, denoted by Ui. Customers are always troubles, and hence my mission is certainly to try my best minimizing their dissatisfaction. If Package i arrives at the destination by ti minutes since I set off from the company, the specific customer’s dissatisfaction should be Ui * ti.
I’m going to minimize the sum of all dissatisfactions. How should I determine the order of deliveries and the tour plan to accomplish the mission?
Input
The input contains several test cases. The number of test cases T (T<=10) occurs in the first line of input.
For each test case:
The first line contains N , M , Vwalk and Twait.
The following line gives the company’s coordinates Cx,Cy .
The following N lines describe the packages, where the ith line contains xi,yi,Ui, indicating that package i locates at Pi(x, y) with a urgency degree Ui.
The following M lines describe the roads. Two formats are potential:
a)Line xiA yiA xiB yiB vi: line-shaped road whose endpoints are Ai(xiA,yiA) and Bi(xiB,yiB).
b)Circle xi yi Ri vi: circle-shaped road whose centre is Oi(xi, yi) with a radius of Ri.
Here, vi denotes the limit speed for the ith road, measured in km/h.
Scales:
1<=N<=15
1<=M<=30
0.01<=Vwalk<=10.00, 0.01<=vi<=120.00, 0.01<=Twait<=60.00
0.01<=Ui, Ri<=1000.00.
The coordinates, radius, time and speed are float numbers and will be rounded in 2 decimals. The absolute value of coordinates doesn’t exceed 1000.
Output
For each test case, output the minimal sum of dissatisfactions in a line, rounded in 2 decimals.
The test data guarantees that answers will not exceed 107.
Sample Input
1
2 5 6 0
3 1
3 0 1
-2 0 1
Circle 0 0 1 60
Line 1 0 2 0 60
Line 2 -1 2 1 60
Line 2 1 -2 1 60
Line 2 -1 -2 -1 60
Sample Output
44.14
题目大意:
一个邮递员位从公司出发,要向N个地点送货物,给出了公司与N个地点的坐标。 同时在地图上还有M条公路,公路为圆环或者是线段,相交的公路是互通的,但不保证所有公路都是互通的。 邮递员每次选择下一个送货地点,有两种到达方式, 一种是直接直线步行到下一个地点,步行的速度是一定的。或者选择坐车,坐车先选择一条起始公路,步行到离这条公路最近的地点,然后等车Twait分钟,然后选择一条结束的道路, 到结束道路离下一个目标最近的位置下车,再步行到目标位置。每次到下一个地点的过程中只能乘坐一次车, 每条路上车的速度是不一定的, 每次乘车的路线要自己制定。
现在对于每个送货地点有一个紧急度ui,也有一个不满意度ui * ti, 其中ti为从公司出发到达第i个送货地点需要的分钟数。制定一种送货策略使得不满意度之和最小。
思路:
这题明显就是防AK了,不过我还是做死写了一写,对着测试数据调了一天一夜还是只有8组数据还是只有3组是对的。只能先说一说自己的思路和coding和debug过程中的tips,等日后AC了再来更新。
首先求出每个送货地点和公司在每条公路上的最近上车位置,圆比较好处理, 注意点在圆心的情况,(有点丧病, 等会儿说圆心。)线段只有可能是点到线段的投影或者是端点,简单判断一下就行了。将点到上车位置连边,时间为Twait + 步行时间。然后对于线段与线段相交, 线段与圆相交, 圆与圆相交都求出交点,对在同一条公路上的点连边。对于圆心,将圆上每个点都与圆心连边。最后就可以求出每个送货地点之间的最短路。再对送货策略进行状态压缩DP。。。
感觉自己根本没说清楚。。。。。。。。。。就当是把代码先保存到这里一下。。。。。。。。。。。。等AC了再细细道来。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <ctime>
#include <bitset>
#include <algorithm>
#include <set>
#include <string>
#include <vector>
#include <list>
#include <stack>
#include <queue>
#include <deque>
using namespace std;
#define LL long long
const double eps = 1e-6;
const double pi = acos(-1.0);
int cmp(double x) {
if(fabs(x) < eps) return 0;
if(x > 0)return 1;
return -1;
}
inline double sqr(double x) {
return x * x;
}
struct point {
double x, y;
point(){}
point(double a, double b): x(a), y(b) {}
void input(){
scanf("%lf%lf",&x,&y);
}
friend point operator + (const point &a, const point &b) {
return point(a.x + b.x, a.y + b.y);
}
friend point operator - (const point &a, const point &b) {
return point(a.x - b.x, a.y - b.y);
}
friend bool operator == (const point &a, const point &b) {
return cmp(a.x - b.x) == 0 && cmp(a.y - b.y) == 0;
}
friend bool operator < (const point &a, const point& b) {
return cmp(a.x - b.x) < 0 || cmp(a.x - b.x) == 0 && cmp (a.y - b.y) < 0;
}
friend point operator * (const point &a, const double &b) {
return point(a.x * b, a.y * b);
}
friend point operator * (const double &a, const point &b) {
return point(a * b.x, a * b.y);
}
friend point operator / (const point &a, const double &b) {
return point(a.x / b, a.y / b);
}
double norm(){
return sqrt(sqr(x) + sqr(y));
}
};
double det(const point &a, const point &b) {
return a.x * b.y - a.y * b.x;
}
double dot(const point &a, const point &b) {
return a.x * b.x + a.y * b.y;
}
double dist(const point &a, const point &b) {
return (a - b).norm();
}
double Angle(point a, point b) {
if(cmp(dot(a, b) - a.norm() * b.norm()) == 0) return 0;
if(cmp(dot(a, b) + a.norm() * b.norm()) == 0) return pi;
return acos(dot(a,b) / a.norm() / b.norm());
}
point rotate_point(const point &p, double A){
double tx = p.x, ty = p.y;
return point(tx * cos(A) - ty * sin(A), tx * sin(A) + ty * cos(A));
}
point rotate_point(const point &p, double sint, double cost) {
double tx = p.x, ty = p.y;
return point(tx * cost - ty * sint, tx * sint + ty * cost);
}
struct line {
point a, b;
line(){}
line(point x, point y):a(x),b(y){}
void input() {
a.input();
b.input();
}
};
void point_pro_line(const point p, const point s, const point t, point &cp) {
double r = dot(t - s, p - s) / dot (t - s, t - s);
cp = s + r * (t - s);
}
bool point_pro_segment(const point p, const point s, const point t, point &cp) {
if(cmp(dot(p - s, t - s))<0) {
cp = s;
return 0;
}
if(cmp(dot(p - t, s - t))<0) {
cp = t;
return 0;
}
double r = dot(t - s, p - s) / dot (t - s, t - s);
cp = s + r * (t - s);
return 1;
}
bool point_on_segment(point p, point s, point t) {
return cmp(det(p - s, t - s))== 0 && cmp(dot(p - s, p - t)) <= 0;
}
bool parallel(line a, line b) {
return !cmp(det(a.a - a.b, b.a - b.b));
}
bool line_cross_line(line a, line b, point &res){
if(parallel(a, b)) return false;
double s1 = det(a.a - b.a, b.b - b.a);
double s2 = det(a.b - b.a, b.b - b.a);
res = (s1 * a.b - s2 * a.a) / (s1 - s2);
return true;
}
int segment_cross_segment(const point& a1,const point& a2,const point& b1,const point& b2, point& res) {
double c1 = det(a2 - a1, b1 - a1);
double c2 = det(a2 - a1, b2 - a1);
double c3 = det(b2 - b1, a1 - b1);
double c4 = det(b2 - b1, a2 - b1);
if (cmp(c1) * cmp(c2) < 0 && cmp(c3) * cmp(c4) < 0) {
res.x = (b1.x * c2 - b2.x * c1) / (c2 - c1);
res.y = (b1.y * c2 - b2.y * c1) / (c2 - c1);
return 1;
}
if(point_on_segment(a1, b1, b2)) {
res = a1;
return 2;
}
if(point_on_segment(a2, b1, b2)) {
res = a2;
return 2;
}
if(point_on_segment(b1, a1, a2)) {
res = b1;
return 2;
}
if(point_on_segment(b2, a1, a2)) {
res = b2;
return 2;
}
return 0;
}
int segment_cross_segment(const line& l1, const line& l2, point& res) {
point a1 = l1.a, a2 = l1.b, b1 = l2.a, b2 = l2.b;
double c1 = det(a2 - a1, b1 - a1);
double c2 = det(a2 - a1, b2 - a1);
double c3 = det(b2 - b1, a1 - b1);
double c4 = det(b2 - b1, a2 - b1);
if (cmp(c1) * cmp(c2) < 0 && cmp(c3) * cmp(c4) < 0) {
res.x = (b1.x * c2 - b2.x * c1) / (c2 - c1);
res.y = (b1.y * c2 - b2.y * c1) / (c2 - c1);
return 1;
}
if(point_on_segment(a1, b1, b2)) {
res = a1;
return 2;
}
if(point_on_segment(a2, b1, b2)) {
res = a2;
return 2;
}
if(point_on_segment(b1, a1, a2)) {
res = b1;
return 2;
}
if(point_on_segment(b2, a1, a2)) {
res = b2;
return 2;
}
return 0;
}
struct circle {
point c;
double r;
circle() {}
circle(point _c, double _r): c(_c), r(_r) {}
void input() {
c.input();
scanf("%lf", &r);
}
double perimeter() {
return 2 * pi * r;
}
double area() {
return sqr(r) * pi;
}
};
bool point_on_circle(const point& p, const circle& c) {
return cmp((p - c.c).norm() - c.r) == 0;
}
bool point_in_circle(const point& p, const circle& c) {
return cmp((p - c.c).norm() - c.r) < 0;
}
int circle_cross_line(line l, circle C, double &t1, double &t2, point res[]) {
double a = l.b.x - l.a.x;
double b = l.a.x - C.c.x;
double c = l.b.y - l.a.y;
double d = l.a.y - C.c.y;
double e = sqr(a) + sqr(c);
double f = 2 * (a * b + c * d);
double g = sqr(b) + sqr(d) - sqr(C.r);
double delta = f * f - 4 * e * g;
if(cmp(delta) < 0) return 0;
if(cmp(delta) == 0) {
t1 = t2 = -f / (2 * e);
res[0] = point(l.a.x + t1 * a, l.a.y + t1 * c);
return 1;
}
t1 = (-f - sqrt(delta)) / (2 * e);
t2 = (-f + sqrt(delta)) / (2 * e);
res[0] = point(l.a.x + t1 * a, l.a.y + t1 * c);
res[1] = point(l.a.x + t2 * a, l.a.y + t2 * c);
return 2;
}
int circle_cross_circle(circle C1, circle C2, point res[]) {
double d = (C1.c - C2.c).norm();
if(cmp(C1.r + C2.r - d) < 0 || cmp(fabs(C1.r - C2.r) - d) > 0) return 0;
if(cmp(C1.r + C2.r - d) == 0 || cmp(fabs(C1.r - C2.r) - d) == 0) {
if(cmp(C2.r - C1.r) > 0) swap(C1, C2);
res[0] = (C2.c - C1.c) / d * C1.r + C1.c;
return 1;
}
double cost = (sqr(C1.r) + sqr(d) - sqr(C2.r)) / (2 * C1.r * d);
double sint = sqrt(1.0 - sqr(cost));
point p = (C2.c - C1.c) / d * C1.r;
res[0] = C1.c + rotate_point(p, cost, -sint);
res[1] = C1.c + rotate_point(p, cost, sint);
return 2;
}
int N, M;
double Vw, Tw;
double vc[2222], vl[2222];
double u[2222];
point p[2222];
int num;
queue<int> q;
bool flag[2222];
bool visit[16][111111];
double f[16][111111];
double us[111111];
int MAXS;
vector<circle>cir;
vector<line>l;
double map[2222][2222];
double dis[2222];
double Search(int k, int S) {
if(visit[k][S]) return f[k][S];
visit[k][S] = 1;
f[k][S] = 1234567890;
if(S == MAXS) return f[k][S] = 0;
for(int i = 1; i <= N; i++) if(((1 << i) & S) == 0) {
double tmp = Search(i, S | (1 << i)) + us[MAXS - S] * map[k][i];
if(tmp < f[k][S]) f[k][S] = tmp;
}
return f[k][S];
}
void init() {
cir.clear();
l.clear();
memset(flag, 0, sizeof(flag));
memset(visit, 0, sizeof(visit));
memset(us, 0, sizeof(us));
}
int main() {
freopen("in", "r", stdin);
int T;
scanf("%d", &T);
while(T--) {
init();
scanf("%d%d%lf%lf", &N, &M, &Vw, &Tw);
Tw /= 60.0;
p[0].input();
for(int i = 1; i <= N; i++) {
p[i].input();
scanf("%lf", &u[i]);
}
string s;
circle cc;
line ll;
for(int i = 0; i < M; i++) {
cin >> s;
if(s == "Circle") {
cc.input();
cir.push_back(cc);
scanf("%lf", &vc[cir.size() - 1]);
}
else {
ll.input();
l.push_back(ll);
scanf("%lf", &vl[l.size() - 1]);
}
}
num = N + 1;
for(int i = 0; i < 2000; i++) {
for(int j = 0; j < 2000; j++) {
map[i][j] = 123456789.0;
}
}
for(int i = 0; i <= N; i++) {
for(int j = 0; j <= N; j++) {
map[i][j] = (p[i] - p[j]).norm() / Vw;
}
}
for(int i = 0; i <= N; i++) {
for(int j = 0; j < (int)l.size(); j++) {
point cp;
point_pro_segment(p[i], l[j].a, l[j].b, cp);
p[num++] = cp;
map[i][num - 1] = min(map[i][num - 1], (cp - p[i]).norm() / Vw + Tw);
map[num - 1][i] = min(map[i][num - 1], (cp - p[i]).norm() / Vw);
}
}
for(int i = 0; i <= N; i++) {
for(int j = 0; j < (int)cir.size(); j++) {
point cp;
if(p[i] == cir[j].c) {
p[num++] = cp;
continue;
}
cp = p[i] - cir[j].c;
cp = cp / cp.norm() * cir[j].r + cir[j].c;
p[num++] = cp;
map[i][num - 1] = min(map[i][num - 1], (cp - p[i]).norm() / Vw + Tw);
map[num - 1][i] = min(map[i][num - 1], (cp - p[i]).norm() / Vw);
}
}
for(int i = 0; i < (int)l.size(); i++) {
for(int j = i + 1; j < (int)l.size(); j++) {
point cp;
if(segment_cross_segment(l[i], l[j], cp)) {
p[num++] = cp;
}
}
}
for(int i = 0; i < (int)l.size(); i++) {
for(int j = 0; j < (int)cir.size(); j++) {
point cp[2];
double t1, t2;
int t = circle_cross_line(l[i], cir[j], t1, t2, cp);
if(t >= 1 && t1 >= 0 && t1 <= 1) {
p[num++] = cp[0];
}
if(t == 2 && t2 >= 0 && t2 <= 1) {
p[num++] = cp[1];
}
}
}
for(int i = 0; i < (int)cir.size(); i++) {
for(int j = i + 1; j < (int)cir.size(); j++) {
point cp[2];
int t = circle_cross_circle(cir[i], cir[j], cp);
if(t >= 1) {
p[num++] = cp[0];
}
if(t == 2) {
p[num++] = cp[1];
}
}
}
for(int i = 0; i <= N; i++) {
for(int j = 0; j < (int)cir.size(); j++) {
if(p[i] == cir[j].c) {
for(int k = 0; k < num; k++) if(point_on_circle(p[k], cir[j])) {
map[i][k] = min(map[i][k], (p[i] - p[k]).norm() / Vw + Tw);
map[k][i] = min(map[k][i], (p[i] - p[k]).norm() / Vw);
}
}
}
}
for(int i = N + 1; i < num; i++) {
for(int j = i + 1; j < num; j++) {
for(int k = 0; k < (int)l.size(); k++) {
if(point_on_segment(p[i], l[k].a, l[k].b) && point_on_segment(p[j], l[k].a, l[k].b)) {
map[i][j] = map[j][i] = min(map[i][j], (p[i] - p[j]).norm() / vl[k]);
}
}
for(int k = 0; k < (int)cir.size(); k++) {
if(point_on_circle(p[i], cir[k]) && point_on_circle(p[j], cir[k])) {
double A = Angle(p[i] - cir[k].c, p[j] - cir[k].c);
map[i][j] = map[j][i] = min(map[i][j], cir[k].perimeter() * A / 2 / pi / vc[k]);
}
}
}
}
for(int i = 0; i <= N; i++) {
for(int j = 0; j < num; j++) dis[j] = 1234567890.0;
memset(flag, 0, sizeof(flag));
while(!q.empty())q.pop();
dis[i] = 0;
q.push(i);
flag[i] = 1;
while(!q.empty()) {
int u = q.front();
flag[u] = 0;
q.pop();
for(int j = 0; j < num; j++) {
if(dis[u] + map[u][j] < dis[j]) {
dis[j] = dis[u] + map[u][j];
if(!flag[j] && j > N) {
q.push(j);
flag[j] = 1;
}
}
}
}
for(int j = 0; j <= N; j++) map[i][j] = dis[j];
}
printf("%d\n", num);
MAXS = (1 << (N + 1)) - 1;
for(int i = 0; i <= MAXS; i++) {
for(int j = 1; j <= N; j++) if((1 << j) & i) {
us[i] += u[j];
}
}
printf("%.2lf\n", Search(0, 1) * 60);
}
return 0;
}