[洛谷春令营 D7T4] 天路【二分答案+SPFA判正权环】

  • 2018-02-21
  • 0
  • 0

Problem:

题目描述

“那是一条神奇的天路诶~,把第一个神犇送上天堂~”,XDM先生唱着这首“亲切”的歌曲,一道猥琐题目的灵感在脑中出现了。

和C_SUNSHINE大神商量后,这道猥琐的题目终于出现在本次试题上了,旨在难到一帮大脑不够灵活的OIer们(JOHNKRAM真的不是说你……)。

言归正传,小X的梦中,他在西藏开了一家大型旅游公司,现在,他要为西藏的各个景点设计一组铁路线。但是,小X发现,来旅游的游客都很挑剔,他们乘火车在各个景点间游览,景点的趣味当然是不用说啦,关键是路上。试想,若是乘火车一圈转悠,却发现回到了游玩过的某个景点,花了一大堆钱却在路上看不到好的风景,那是有多么的恼火啊。

所以,小X为所有的路径定义了两个值,Vi和Pi,分别表示火车线路的风景趣味度和乘坐一次的价格。现在小X想知道,乘客从任意一个景点开始坐火车走过的一条回路上所有的V之和与P之和的比值的最大值。以便为顾客们推荐一条环绕旅游路线(路线不一定包含所有的景点,但是不可以存在重复的火车路线)。

于是,小X梦醒之后找到了你……

输入输出格式

输入格式:

第一行两个正整数N,M,表示有N个景点,M条火车路线,火车路线是单向的。

以下M行,每行4个正整数,分别表示一条路线的起点,终点,V值和P值。

注意,两个顶点间可能有多条轨道,但一次只能走其中的一条。

输出格式:

一个实数,表示一条回路上最大的比值,保留1位小数。

若没有回路,输出-1。

输入输出样例

输入样例#1:

5 6
1 2 1 1
4 1 6 2
5 4 8 1
2 3 2 2
5 2 4 1
3 5 6 4

输出样例#1:

2.3

说明

对于30%的数据,1≤N≤100,1≤M≤20;

对于60%的数据,1≤N≤3,000,1≤M≤2,000;

对于100%的数据,1≤N≤7,000,1≤M≤20,000,1≤Vi,Pi≤1,000.

保证答案在200以内.

Solution:

这是一道最大化比例问题的经典模型,考虑将 2 个变量削减成 1 个。

我们设比值最大为 ans,则对于所有回路有

  • ∑ Vi / ∑ Pi ≤ ans
  • ∑ Vi ≤ ans * ∑ Pi
  • ∑ Vi - ans * ∑ Pi ≤ 0
  • ∑ (Vi  - ans *  Pi) ≤ 0

由于本题 ans 的范围在 (0, 200] 之间,我们可以二分 ans,以 Vi  - ans *  Pi 作为边权建图。

然后跑一遍 DFS-SPFA 来判断图中是否有正权环,如果有则 ans 不够紧确,否则太大。

温馨提示:答案是 double 型的,所以边权也要用 double 来存。

Code: O(能过) [2605K, 8MS]

/*
	∑V[i] / ∑P[i] <= ans
=>	∑V[i] <= ans * ∑P[i]
=>	∑(V[i] - ans * P[i]) <= 0
*/
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define eps 1e-3
using namespace std;

inline void getint(int &num){
	char ch;
	while(!isdigit(ch = getchar()));
	num = ch - '0';
	while(isdigit(ch = getchar())) num = num * 10 + ch - '0';
}

int N, M;
int S[20005], T[20005], V[20005], P[20005];

struct Edge{
	int np;
	double val;
	Edge *nxt;
};

struct Graph{
	Edge E[20005], *V[7005];
	int tope;
	
	inline void clear() {tope = 0, memset(V, 0, sizeof(V));}
	
	inline void addedge(int u, int v, double w){
		E[++tope].np = v, E[tope].val = w;
		E[tope].nxt = V[u], V[u] = &E[tope];
	}
} G;

double dis[7005];
bool inpath[7005];

inline bool dfs_spfa(int u){
	inpath[u] = 1;
	for(register Edge *ne = G.V[u]; ne; ne = ne->nxt)
		if(dis[u] + ne->val > dis[ne->np]){
			dis[ne->np] = dis[u] + ne->val;
			if(inpath[ne->np]) return 1;
			if(dfs_spfa(ne->np)) return 1;
		}
	return inpath[u] = 0, 0;
}

inline bool check(double k){
	G.clear();
	for(register int i = 1; i <= M; i++)
		G.addedge(S[i], T[i], V[i] - k * P[i]);
	memset(dis, 0, sizeof(dis));
	memset(inpath, 0, sizeof(inpath));
	for(register int i = 1; i <= N; i++)
		if(dfs_spfa(i)) return 1;
	return 0;
}

int main(){
	getint(N), getint(M);
	for(register int i = 1; i <= M; i++)
		getint(S[i]), getint(T[i]), getint(V[i]), getint(P[i]);
	double l = 0.0, r = 200.0;
	while(l + eps < r){
		double mid = (l + r) / 2;
		if(check(mid)) l = mid;
		else r = mid;
	}
	if(l < eps) puts("-1");
	else printf("%.1f\n", l);
	return 0;
}

评论

还没有任何评论,你来说两句吧



新博客地址: darkleafin.cf

常年不在线的QQ:
49750

不定期更新的GitHub:
https://github.com/Darkleafin


OPEN AT 2017.12.10

如遇到代码不能正常显示的情况,请刷新页面。
Please refresh the page if the code cannot be displayed normally.


发现一个优美的网站:
https://visualgo.net/en
















- Theme by Qzhai