POJ 3621 SPFA

题意:给n个点的点权以及m条有向边,求图中一个 点权和与边权和之比 最大的环的 点权和与边权和之比。

思路:与环有关的可以想到SPFA,

           设点权为c,i到j的边权为w[i][j],

           设答案为ans,点1~p依次连成所求环, 则,

           (c[1] + c[2] + ... + c[p]) / (w[1][2] + w[2][3] + ... + w[p][1]) = ans;

            ans不好直接求,考虑二分答案,若当前判断是否存在 点权和与边权和之比 > t,

            则重新定义从 i 到 j 边权为: w[i][j] * t - c[j],

            此时若存在负权环,则考虑这个环的边权之和,

            (w[1][2] + w[2][3]... + w[p][1]) * t - (c[1] ... + c[p]) < 0

            t < (c[1]  ... + c[p]) / (w[1][2] + w[2][3] ... + w[p][1]) = ans, 即答案一定比 t 大,

            若不存在反之。

            注意最好用%f输出。。。

代码:

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

#define For(i,j,k) for(int i = j;i <= (int)k;i ++)
#define Set(i,j) memset(i, j, sizeof(i))
#define pb push_back

using namespace std;
const int N = 1010;

vector<int> G[N], w[N];
int n, m, c[N], inq[N];
double d[N];

void Add(int x, int y, int z){
	G[x].pb(y), w[x].pb(z);
}

bool SPFA(double val){
	queue<int> q;
	For(i,0,n) d[i] = 1e18;
	Set(inq, 0);
	inq[1] = -1, d[1] = 0, q.push(1);
	while(!q.empty()){
		int s = q.front(); q.pop();
		inq[s] = -inq[s];
		For(i,0,G[s].size() - 1){
			int v = G[s][i];
			double dis = val * w[s][i] - c[v];
			if(dis + d[s] < d[v]){
				d[v] = dis + d[s];
				if(inq[v] >= 0){
					if(inq[v] >= n) return 1;
					inq[v] = -inq[v] - 1;
					q.push(v);
				}
			}
		}
	}
	return 0;
}

int main(){
	scanf("%d%d", &n, &m);
	For(i,1,n) scanf("%d", &c[i]);
	For(i,1,m){
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		Add(x, y, z);
	}
	double L = 0, R = N, M;
	while(R - L > 1e-4){
		M = (L + R) / 2;
		if(SPFA(M)) L = M;
		else R = M;
	}
	printf("%.2f", M);
	return 0;
}



  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值