Sightseeing Cows G

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(2L1000) 座标志性建筑物(建筑物按 1 … L 1\dots L 1L 顺次编号),以及连接这些建筑物的 P ( 2 ⩽ P ⩽ 5000 ) P(2\leqslant P\leqslant5000) P(2P5000) 条道路。按照计划,那天早上 Farmer John 会开车将奶牛们送到某个她们指定的建筑物旁边,等奶牛们完成她们的整个旅行并回到出发点后,将她们接回农场。由于大城市中总是寸土寸金,所有的道路都很窄,政府不得不把它们都设定为通行方向固定的单行道。
尽管参观那些标志性建筑物的确很有意思,但如果你认为奶牛们同样享受穿行于大城市的车流中的话,你就大错特错了。与参观景点相反,奶牛们把走路定义为无趣且令她们厌烦的活动。对于编号为ii的标志性建筑物,奶牛们清楚地知道参观它能给自己带来的乐趣值 F i ( 1 ⩽ F i ⩽ 1000 ) F_i (1\leqslant F_i\leqslant1000) Fi(1Fi1000) 。相对于奶牛们在走路上花的时间,她们参观建筑物的耗时可以忽略不计。
奶牛们同样仔细地研究过城市中的道路。她们知道第i条道路两端的建筑物 L 1 i L1_i L1i L 2 i L2_i L2i (道路方向为 L 1 i → L 2 i L1_i \rightarrow L2_i L1iL2i ),以及她们从道路的一头走到另一头所需要的时间 T i ( 1 ⩽ T i ⩽ 1000 ) T_i(1\leqslant T_i\leqslant1000) Ti(1Ti1000)
为了最好地享受她们的休息日,奶牛们希望她们在一整天中平均每单位时间内获得的乐趣值最大。当然咯,奶牛们不会愿意把同一个建筑物参观两遍,也就是说,虽然她们可以两次经过同一个建筑物,但她们的乐趣值只会增加一次。顺便说一句,为了让奶牛们得到一些锻炼, 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 i1ttime[to]i1tfun[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] i1tfun[from]>midi1ttime[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] midi1ttime[to]<i1tfun[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 midi1ttime[to]i1tfun[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 i1t(midtime[to]fun[from])<0(把 ∑ i − 1 t \sum_{i-1}^{t} i1t 提出来)
那就变成求有没有负环。

又因为是小数,所以二分的时候二分 ∗ 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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/108126787