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;
}