poj3621_判断负环_01分数规划

本文解析了一道经典的图论问题“观光牛”,目标是寻找一条闭环路径,使点权之和与边权之比最大化。通过将点权转化为边权,并运用二分查找和SPFA算法,最终求解最大乐趣比。

题目:http://poj.org/problem?id=3621

Sightseeing Cows
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 12563 Accepted: 4259
Description

Farmer John has decided to reward his cows for their hard work by taking them on a tour of the big city! The cows must decide how best to spend their free time.

Fortunately, they have a detailed city map showing the L (2 ≤ L ≤ 1000) major landmarks (conveniently numbered 1… L) and the P (2 ≤ P ≤ 5000) unidirectional cow paths that join them. Farmer John will drive the cows to a starting landmark of their choice, from which they will walk along the cow paths to a series of other landmarks, ending back at their starting landmark where Farmer John will pick them up and take them back to the farm. Because space in the city is at a premium, the cow paths are very narrow and so travel along each cow path is only allowed in one fixed direction.

While the cows may spend as much time as they like in the city, they do tend to get bored easily. Visiting each new landmark is fun, but walking between them takes time. The cows know the exact fun values Fi (1 ≤ Fi ≤ 1000) for each landmark i.

The cows also know about the cowpaths. Cowpath i connects landmark L1i to L2i (in the direction L1i -> L2i ) and requires time Ti (1 ≤ Ti ≤ 1000) to traverse.

In order to have the best possible day off, the cows want to maximize the average fun value per unit time of their trip. Of course, the landmarks are only fun the first time they are visited; the cows may pass through the landmark more than once, but they do not perceive its fun value again. Furthermore, Farmer John is making the cows visit at least two landmarks, so that they get some exercise during their day off.

Help the cows find the maximum fun value per unit time that they can achieve.

Input

  • Line 1: Two space-separated integers: L and P
  • Lines 2…L+1: Line i+1 contains a single one integer: Fi
  • Lines L+2…L+P+1: Line L+i+1 describes cow path i with three space-separated integers: L1i , L2i , and Ti

Output

  • Line 1: A single number given to two decimal places (do not perform explicit rounding), the maximum possible average fun per unit time, or 0 if the cows cannot plan any trip at all in accordance with the above rules.

Sample Input

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
Sample Output

6.00

题目大意:
有一张图,图中有n个点,m条边。图中可能有环;让我们找到一个环,这个环的所有点权之和 / 所有边权之和 最大的那个环;最后输出这个除法的结果。

解题思路:
首先设一个环中点权之和为x, 边权之和为y, 那么设 mid * y - x < 0 那么 x / y > mid,所以这个mid对于这个x,y来说并不是答案,真正大答案要大于mid,如果mid * y - x > 0,说明mid > 答案,所以这个mid是由单调性的,根据这个性质我们可以推广到所有的点,我们的目的就是要找到一个最大的mid使得图中所有的 mid * y - x >= 0, 并且存在 mid * y - x == 0。 这样的话我们就可以把点权放到边上,假如有一条边为a -> b, 那么w[a->b] = mid * w[a->b] - node[a]; 即 mid * 原来的边权 - 出点的点权;最后我们在l,r这个范围进行二分去找答案就可以了,每次判断的条件是 只要图中存在负环,就让 l = mid, 否则让 r = mid;最后输出l;

注意点:
1.我们在实现的时候可以不断地重新建立新的图,也可以直接在原图上进行比较。我采用的是第二种方法,具体看代码;
2.eps = 1e-4;
3.注意数据的类型,以及输入

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

const int N = 1005, M = 5005;
const double eps = 1e-4;

int n, m;
int node[N];
double l, r;

int h[N], e[M], w[M], ne[M], idx;
inline void add(int a, int b, int c) {
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

bool vis[N];
double d[N];
int cnt[N];

inline bool spfa(double mid) {
	for(int i = 1; i <= n; i ++) {
		cnt[i] = 0;
		d[i] = 0;
		vis[i] =false;
	}
	
	queue<int> q;
	while(q.size()) q.pop();
	
	for(int i = 1; i <= n; i ++) {
		q.push(i);
		vis[i] = true;
	}
	
	while(q.size()) {
		int u = q.front(); q.pop();
		vis[u] = false;
		
		for(int i = h[u]; i + 1; i = ne[i]) {
			int v = e[i];
			if(d[v] > d[u] + mid * w[i] - node[u]) {
				d[v] = d[u] + mid * w[i] - node[u];
				cnt[v] = cnt[u] + 1;
				if(cnt[v] >= n) return true;
				if(!vis[v]) q.push(v), vis[v] = true;
			}
		}
	}
	
	return false;
}

inline void find(void) {
	while(r - l > eps) {
		double mid = (l + r) / 2;
		if(spfa(mid)) l = mid;
		else r = mid;
	}
}

int main(void) {
	scanf("%d%d", &n, &m);
	memset(h, -1, sizeof h);
	for(int i = 1; i <= n; i ++) 
		scanf("%d", &node[i]), r += node[i];
	for(int i = 1; i <= m;  i++) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
	}
	
	l = 1;
	find();
	printf("%.2lf\n", l);
	
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值