0915-分数规划(最优比率环)-POJ 3621

传送门

大致题意

一个有向图,求其最优的一个环,所谓最优就是  这个环上的点权之和比上边权之和的值最大

分析

做了前面两道题后,到了第三道题,就会感觉分数规划的题思路大致都一样,主要在于定义好 a(x)和 b (x)后,怎么和这道题联系在一起

我们来看,一个环上,其顶点个数和边的条数肯定是一样的,那对于任意一条边,我们就强制规定其新权值为 起点的点权 - r * 该边边权,然后在此基础上如果图中存在一个正权环,那么 r 还可以调大

但怎么有效的判断正权环的存在呢???

 

                                         思考                                                               

 

正确答案:正难则反

 

判断正权环我还没学过,但负环的话总是会的,用spfa搞一下就可以了。于是我们重新定义新权值为    r * 该边边权-起点的点权

 

此时如果存在负环,则原图应该存在正环,则 r 调大

 

代码

 

还是有一点点的坑

 

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
#define ll long long
#define N 1004
#define M 5004
using namespace std;
int n,m,f[N];
int nxt[M],head[N],to[M],dis[M],tot,from[M];
double w[M],d[N];
int vis[N],in[N];
void add(int x,int y,int z){
	nxt[++tot]=head[x];head[x]=tot;
	from[tot]=x;to[tot]=y;dis[tot]=z;
}
bool spfa(double r){
	queue<int > q;memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;++i) d[i]=1e15;
	memset(in,0,sizeof(in));
	for(int i=1;i<=tot;++i) w[i]=r*dis[i]-f[from[i]];
	vis[1]++;d[1]=0;
	q.push(1);in[1]=1;
	while(!q.empty()){
		int u=q.front();q.pop();in[u]=0;
		for(int e=head[u];e;e=nxt[e]){
			int v=to[e];
			if(d[v]>d[u]+w[e]){
				d[v]=d[u]+w[e];
				if(!in[v]){	q.push(v);in[v]=1;	}
				vis[v]++;
				if(vis[v]>n) return 1;
			}
		}
	}
	return 0;
}
int main(){
	scanf("%d%d",&n,&m);
	int i,j,k,x,y,z;
	for(i=1;i<=n;++i) scanf("%d",&f[i]);
	for(i=1;i<=m;++i){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
	}
	double l=0.0,r=10000.0;
	// L-R < 1e-3这样写就不可以,会死循环,可能是精度的问题吧
	while(r-l>1e-3){
		double mid=(l+r)/2.0;
		if(spfa(mid)) l=mid;
		else r=mid;
	}
	printf("%.2f",l);
	return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值