分式规划问题(二分+Bellman图论(spfa))3

TZOJ:6544

题目大意是要求一个最小值 μ,定义为图中环的(边权值之和)/(该环内的点数)。

分式规划问题一般都是二分,所以先把二分模板写出来,然后开始思考怎么判断该二分答案x是否可行。

首先可以得到这个公式

属于这个环的边权值之和除以这个环内的点数要小于等于当前二分答案x。

化简得到:

由于权值的累加要减掉x*k,实际上就是每个边权值都减去k,就得到了

所以,Wi-x就可以作为他的权值使用,那么我们只需要发现至少一个负环,就说明当前二分答案是可行的。

求负环的方法,需要用的bellman(spfa)算法,用于判断图内有无负环产生,并求出起点到各个点的最小值。

其中dfs就是bellman算法,存储也要用稀疏图去存储,遍历每一条边的情况,如果出现更短的路,就要继续dfs下去,直到没有更短路被更新或者被更新点已经走过,就说明有最短路产生。

#include<bits/stdc++.h>
using namespace std;
#define double long double
const int N=3e3+10;
int n,m,bj[N];
double a[N];
struct zuo
{
	int y;
	double z;
}p;
vector<struct zuo>s[N];

int dfs(int x,double t)
{
	bj[x]=1;
	for(int i=0;i<s[x].size();i++)
	{
		if(a[s[x][i].y]>a[x]+s[x][i].z-t)
		{
			a[s[x][i].y]=a[x]+s[x][i].z-t;
			if(bj[s[x][i].y]||dfs(s[x][i].y,t))return 1;
		}
	}
	bj[x]=0;
	return 0;
}
int check(double x)
{
	memset(bj,0,sizeof(bj));
	for(int i=1;i<=n;i++)a[i]=1e9; 
	for(int i=1;i<=n;i++)
		if(dfs(i,x))return 1;
	return 0;
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int x;
		cin>>x>>p.y>>p.z;
		s[x].push_back(p);
	} 
	double l=-1e7,r=1e7;
	while(r-l>1e-10)
	{
		double mid=(l+r)/2;
		if(check(mid))r=mid;
		else l=mid;
	}
	cout<<setiosflags(ios::fixed);
	cout<<setprecision(8)<<r+1e-10<<endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值