AcWing 361. 观光奶牛

题目

给定一张L个点、P条边的有向图,每个点都有一个权值f[i],每条边都有一个权值t[i]。

求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。

输出这个最大值。

注意:数据保证至少存在一个环。

输入格式

第一行包含两个整数L和P。

接下来L行每行一个整数,表示f[i]。

再接下来P行,每行三个整数a,b,t[i],表示点a和b之间存在一条边,边的权值为t[i]。

输出格式

输出一个数表示结果,保留两位小数。

数据范围

2≤L≤1000,
2≤P≤5000,
1≤f[i],t[i]≤1000

思路

01分数规划。
在这里插入图片描述
如果①成立证明答案可能更大,二分答案将l向右移,否则向左移。只要证明有正环就证明答案大于mid。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;

const int N=1010,M=5010;
int n,m;
int wf[N];
int h[N],e[M],wt[M],ne[M],idx;
double dist[N];
int q[N],cnt[N];
bool st[N];

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

bool check(double mid)
{
	memset(dist,0,sizeof dist);
	memset(st,0,sizeof st);
	memset(cnt,0,sizeof cnt);
	
	int hh=0,tt=0;
	for(int i=1;i<=n;i++)
	{
		q[tt++]=i;
		st[i]=true;
	}
	while(hh!=tt)
	{
		int t=q[hh++];
		if(hh==N) hh=0;
		st[t]=false;
		for(int i=h[t];~i;i=ne[i])
		{
			int j=e[i];
			if(dist[j]<dist[t]+wf[t]-mid*wt[i])
			{
				dist[j]=dist[t]+wf[t]-mid*wt[i];
				cnt[j] = cnt[t]+1;
				if(cnt[j]>=n) return true;
				if(!st[j])
				{
					q[tt++]=j;
					if(tt==N) tt=0;
					st[j]=true;
				}
			}
		}
	}
	return false;
}

int main()
{
	//freopen("test.in","r",stdin);//设置 cin scanf 这些输入流都从 test.in中读取
    //freopen("test.out","w",stdout);//设置 cout printf 这些输出流都输出到 test.out里面去
	//ios::sync_with_stdio(false);
	//cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>wf[i];
	memset(h,-1,sizeof h);
	for(int i=0;i<m;i++)
	{
		int a,b,c;
		cin>>a>>b>>c;
		add(a,b,c);
	}
	double l=0,r=1e6;
	while(r-l>1e-4)
	{
		double mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	printf("%.2lf\n",l);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44828887/article/details/107271543