Sightseeing Cows G \operatorname{Sightseeing\ Cows\ G} Sightseeing Cows G
题目链接: luogu P2868 \operatorname{luogu\ P2868} luogu P2868
题目翻译
作为对奶牛们辛勤工作的回报, Farmer John 决定带她们去附近的大城市玩一天。旅行的前夜,奶牛们在兴奋地讨论如何最好地享受这难得的闲暇。
很幸运地,奶牛们找到了一张详细的城市地图,上面标注了城市中所有
L
(
2
⩽
L
⩽
1000
)
L(2\leqslant L\leqslant1000)
L(2⩽L⩽1000) 座标志性建筑物(建筑物按
1
…
L
1\dots L
1…L 顺次编号),以及连接这些建筑物的
P
(
2
⩽
P
⩽
5000
)
P(2\leqslant P\leqslant5000)
P(2⩽P⩽5000) 条道路。按照计划,那天早上 Farmer John 会开车将奶牛们送到某个她们指定的建筑物旁边,等奶牛们完成她们的整个旅行并回到出发点后,将她们接回农场。由于大城市中总是寸土寸金,所有的道路都很窄,政府不得不把它们都设定为通行方向固定的单行道。
尽管参观那些标志性建筑物的确很有意思,但如果你认为奶牛们同样享受穿行于大城市的车流中的话,你就大错特错了。与参观景点相反,奶牛们把走路定义为无趣且令她们厌烦的活动。对于编号为ii的标志性建筑物,奶牛们清楚地知道参观它能给自己带来的乐趣值
F
i
(
1
⩽
F
i
⩽
1000
)
F_i (1\leqslant F_i\leqslant1000)
Fi(1⩽Fi⩽1000) 。相对于奶牛们在走路上花的时间,她们参观建筑物的耗时可以忽略不计。
奶牛们同样仔细地研究过城市中的道路。她们知道第i条道路两端的建筑物
L
1
i
L1_i
L1i 和
L
2
i
L2_i
L2i (道路方向为
L
1
i
→
L
2
i
L1_i \rightarrow L2_i
L1i→L2i ),以及她们从道路的一头走到另一头所需要的时间
T
i
(
1
⩽
T
i
⩽
1000
)
T_i(1\leqslant T_i\leqslant1000)
Ti(1⩽Ti⩽1000) 。
为了最好地享受她们的休息日,奶牛们希望她们在一整天中平均每单位时间内获得的乐趣值最大。当然咯,奶牛们不会愿意把同一个建筑物参观两遍,也就是说,虽然她们可以两次经过同一个建筑物,但她们的乐趣值只会增加一次。顺便说一句,为了让奶牛们得到一些锻炼, Farmer John 要求奶牛们参观至少
2
2
2 个建筑物。
请你写个程序,帮奶牛们计算一下她们能得到的最大平均乐趣值。
输入
输出
样例输入
5 7
30
10
10
5
10
1 2 3
2 3 2
3 4 5
3 5 2
4 5 5
5 1 3
5 2 2
样例输出
6.00
思路
这道题是一道分数规划的模板题。
我们二分最大平均乐趣值,那
m
i
d
mid
mid 可以,就是会有一个环,让:
∑
i
−
1
t
f
u
n
[
f
r
o
m
]
∑
i
−
1
t
t
i
m
e
[
t
o
]
>
m
i
d
\frac{\sum_{i-1}^{t}fun[from]}{\sum_{i-1}^{t}time[to]}>mid
∑i−1ttime[to]∑i−1tfun[from]>mid
化公式:
∑
i
−
1
t
f
u
n
[
f
r
o
m
]
>
m
i
d
∗
∑
i
−
1
t
t
i
m
e
[
t
o
]
\sum_{i-1}^{t}fun[from]>mid*\sum_{i-1}^{t}time[to]
∑i−1tfun[from]>mid∗∑i−1ttime[to](移分母)
m
i
d
∗
∑
i
−
1
t
t
i
m
e
[
t
o
]
<
∑
i
−
1
t
f
u
n
[
f
r
o
m
]
mid*\sum_{i-1}^{t}time[to]<\sum_{i-1}^{t}fun[from]
mid∗∑i−1ttime[to]<∑i−1tfun[from](左右互换)
m
i
d
∗
∑
i
−
1
t
t
i
m
e
[
t
o
]
−
∑
i
−
1
t
f
u
n
[
f
r
o
m
]
<
0
mid*\sum_{i-1}^{t}time[to]-\sum_{i-1}^{t}fun[from]<0
mid∗∑i−1ttime[to]−∑i−1tfun[from]<0(右边的移到左边)
∑
i
−
1
t
(
m
i
d
∗
t
i
m
e
[
t
o
]
−
f
u
n
[
f
r
o
m
]
)
<
0
\sum_{i-1}^{t}(mid*time[to]-fun[from])<0
∑i−1t(mid∗time[to]−fun[from])<0(把
∑
i
−
1
t
\sum_{i-1}^{t}
∑i−1t 提出来)
那就变成求有没有负环。
又因为是小数,所以二分的时候二分
∗
10000
*10000
∗10000 的数,
s
p
f
a
spfa
spfa 求负环和输出的时候再
/
10000
/10000
/10000 。
就可以了。
代码
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
struct node {
int x, to, nxt;
}e[50001];
int n, p, fun[1001], x, y, z, le[1001], KK;
int l, r = 2147483647, mid, len[1001], ans;
double dis[1001];
bool in[1001];
void add(int x, int y, int z) {
e[++KK] = (node){z, y, le[x]}; le[x] = KK;
}
bool ch(double midd) {
queue <int> q;
memset(in, 0, sizeof(in));
memset(len, 0, sizeof(len));
memset(dis, 0x7f, sizeof(dis));//清零
q.push(1);//初始化
in[1] = 1;
dis[1] = 0;
while (!q.empty()) {//spfa
int now = q.front();
q.pop();
for (int i = le[now]; i; i = e[i].nxt)
if (dis[e[i].to] > dis[now] + (double)(e[i].x * midd - fun[now])) {
dis[e[i].to] = dis[now] + (double)(e[i].x * midd - fun[now]);//边权的长度变成e[i].x * midd - fun[now]
len[e[i].to] = len[now] + 1;
if (len[e[i].to] == n) return 1;//判断有没有负环
if (!in[e[i].to]) {
in[e[i].to] = 1;
q.push(e[i].to);
}
}
in[now] = 0;
}
return 0;
}
int main() {
scanf("%d %d", &n, &p);//读入
for (int i = 1; i <= n; i++)
scanf("%d", &fun[i]);
for (int i = 1; i <= p; i++) {
scanf("%d %d %d", &x, &y, &z);
add(x, y, z);//建图
}
while (l <= r) {//二分
mid = (l + r) / 2;
if (ch((double)mid / 10000)) {
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
printf("%.2lf", (double)ans / 10000);//输出
return 0;
}