【负环】【01分数规划】【例题向】观光奶牛

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_42725189/article/details/102657506
  • 题目链接acwing361
  • 题意概括:给定一个有向图,点权、边权,求一个环使得它的点权和除以边权和最大。
  • 思路:01分数规划的思想,先二分L值,然后跑最长路spfa判正环(相当于01分数规划中使得相减后的权值最大)
  • spfa判正环:记录一个数组cnt[i],表示从1到i的最长路中有cnt[i]条边。每次更新dis值时更新cnt:cnt[e[i].to]=cnt[zz]+1。当cnt大于等于n(注意,这里是点数)说明有负环。
  • 注意:存在易错点,在代码中标出。
#include<bits/stdc++.h>
using namespace std;
struct node{
	int to,nxt,val;
}e[10005];
int n,m,f[1005],tot,head[1005];
void build(int a,int b,int c)
{
	e[++tot].to=b;
	e[tot].val=c;
	e[tot].nxt=head[a];
	head[a]=tot;
}
int cnt[1005];
double dis[1005];
bool vis[1005],inq[1005];
queue<int>q;
bool check(double v)
{
	memset(vis,0,sizeof(vis));
	memset(dis,-0x3f,sizeof(dis));//最长路要把dis初值赋值为负
	memset(inq,0,sizeof(inq));//一定要清零!因为在跑spfa时中途要退出
	for(int zz,i=1;i<=n;i++)
	{
		if(!vis[i])
		{
			while(!q.empty())q.pop();
			dis[i]=0;
			cnt[i]=0;
			q.push(i);
			while(!q.empty())
			{
				zz=q.front();
				q.pop();
				inq[zz]=false;
				vis[zz]=true;
				for(int j=head[zz];j;j=e[j].nxt)
				{
					if(dis[e[j].to]<dis[zz]+f[e[j].to]-e[j].val*v)
					//因为是点权和除以边权和,所以注意距离的转移
					{
						dis[e[j].to]=dis[zz]+f[e[j].to]-e[j].val*v;
						cnt[e[j].to]=cnt[zz]+1;
						if(cnt[e[j].to]>=n)
						return true;
						if(!inq[e[j].to])
						{
							inq[e[j].to]=true;
							q.push(e[j].to);
						}
					}
				}
			}
		}
	}
	return false;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	scanf("%d",&f[i]);
	for(int a,b,c,i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		build(a,b,c);
	}
	double l=0,r=100000,mid;
	while(r-l>1e-7)
	{
		mid=(l+r)/2;
		if(check(mid))
		{
			l=mid;
		}
		else
		r=mid;
	}
	printf("%.2lf",l);
}

猜你喜欢

转载自blog.csdn.net/qq_42725189/article/details/102657506