【最短路】UVA11090 Going in Cycle!!解题报告

link

题目大意:

给出一个 N N N个点 M M M条边的有向图,求边权平均值最小的连通块。

题目分析:

首先,每个连通块的边权平均值为: x = ∑ k = i j a k j − i + 1 x=\frac{\sum\limits_{k=i}^{j}a_{k}}{j-i+1} x=ji+1k=ijak ( i i i j j j联通, a i a_{i} ai表示边权)。题目要求平均值最小,即, a n s > = ∑ k = i j a k j − i + 1 ans>=\frac{\sum\limits_{k=i}^{j}a_{k}}{j-i+1} ans>=ji+1k=ijak( i i i j j j联通) 。同时, a n s ans ans是具有单调性的,所以我们考虑二分答案

我们二分出一个 a n s ans ans。考虑对原式子进行化简:

a n s > = ∑ k = i j a k j − i + 1 ans>=\frac{\sum\limits_{k=i}^{j}a_{k}}{j-i+1} ans>=ji+1k=ijak( i i i j j j联通)

a n s ∗ ( j − i + 1 ) > = ∑ k = i j a k ans*(j-i+1)>=\sum\limits_{k=i}^{j}a_{k} ans(ji+1)>=k=ijak

a n s ∗ ( j − i + 1 ) > = a i + a i + 1 + . . . + a j − 1 + a j ans*(j-i+1)>=a_{i}+a_{i+1}+...+a_{j-1}+a{j} ans(ji+1)>=ai+ai+1+...+aj1+aj

( a i − a n s ) + ( a i + 1 − a n s ) + . . . + ( a j − 1 − a n s ) + ( a j − a n s ) &lt; = 0 (a_{i}-ans)+(a_{i+1}-ans)+...+(a_{j-1}-ans)+(a_{j}-ans)&lt;=0 (aians)+(ai+1ans)+...+(aj1ans)+(ajans)<=0

即,当我们二分出的一个答案 a n s ans ans在①式成立的情况下是合法的。

现在我们考虑如何维护①式。

观察可知,我么可以将原来的边权 a i a_{i} ai转化为 a i − a n s a_{i}-ans aians,然后判断在这个新图里面是否有负环,如果有,那么①式肯定成立。即我们二分出来的这个答案 a n s ans ans合法。

注意事项:

1.用 S P F A SPFA SPFA判断是否存在负环是通过记录每个点入队的次数来实现的。如果一个点被超过 n n n个点更新,则一定存在负环。

2.这个图有可能不连通。(比如存在 1 − &gt; 2 , 3 − &gt; 2 1-&gt;2,3-&gt;2 1>2,3>2这种情况)所以在 S P F A SPFA SPFA之前,需要将所有的点加入队列。

3. S P F A SPFA SPFA可以用双端队列或者优先队列优化。

4.由于我们二分的是一个实数,所以需要 e p s eps eps来确定精度。

5.由于是多组数据,所以要注意初始化。

6.如果我们 a n s = M a x e d g e + 1 ans=Maxedge+1 ans=Maxedge+1仍没有负环,则说明该图没有环。即输出"No cycle found."

代码如下:

#include <bits/stdc++.h>
#define inf 1e8+7.0
#define eps 1e-8 
using namespace std;
const int maxn=1e4+5;


double dis[maxn];
int used[maxn],head[maxn],num[maxn];
double l,r;
int size=0,n,m,cas,t;

struct EDGE
{
	int u,v,nxt;
	double w;
}edge[maxn];

void add(int u,int v,double w)
{
	edge[size].u=u;
	edge[size].v=v;
	edge[size].w=w;
	edge[size].nxt=head[u];
	head[u]=size++;
}


bool SPFA()
{
	deque<int>q;
	for(int i=1;i<=n;i++)
	{
		used[i]=0;
		num[i]=0;
	}
	for(int i=1;i<=n;i++)
	q.push_back(i),dis[i]=0;
	
	while(!q.empty())
	{
		int u=q.front();
		q.pop_front();
		used[u]=0;
		for(int i=head[u];~i;i=edge[i].nxt)
		{
			int v=edge[i].v;
			if(dis[v]-(dis[u]+edge[i].w)>eps)
			{
				dis[v]=dis[u]+edge[i].w;
				if(used[v])continue;
				used[v]=0;
				if(!q.empty())
				{
					if(dis[q.front()]-dis[v]>eps)q.push_front(v);
					else q.push_back(v);
				}
				else q.push_back(v);
				num[v]++;
				if(num[v]>n)return true;
			}
		}
	}
	return false;
}

bool check(double mid)
{
	for(int i=0;i<size;i++)
	{
		edge[i].w-=mid;
	}
	bool flag=SPFA();
	for(int i=0;i<size;i++)
	{
		edge[i].w+=mid;
	}
	return flag;
}

void init()
{
	freopen("in.in","r",stdin);
}

void readdata()
{
	memset(head,-1,sizeof(head));
	memset(edge,0,sizeof(edge));
	size=0;
	l=inf,r=-inf;
	scanf("%d%d",&n,&m);
	int u,v;
	double w;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%lf",&u,&v,&w);
		add(u,v,w);
		r=max(r,w);
		l=min(l,w);
	}
	printf("Case #%d: ",++cas);
}

void work()
{
	if(!check(r+1))
	{
		printf("No cycle found.\n");  
        return;
	}	
	while(r-l>=eps)
	{
		double mid=l+(r-l)/2;
		if(check(mid))
		{
			r=mid;
		}
		else 
		{
			l=mid;
		}
	}
	printf("%.2lf\n",r);
}


int main()
{
	init();
	scanf("%d",&t);
	while(t--)
	{
		readdata();
		work();
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值