2019.10.03日常总结

洛谷 P 3199 P3199 P3199:

【题意】:
2019.10.03日常总结_第1张图片
【思路】:
像这种求最大、最小平均值的题目,我们可以考虑二分
像本题,我们可以二分答案 m i d mid mid,然后把所有边的长度减去 m i d mid mid,然后我们对于每个点,都跑一遍 S P F A SPFA SPFA判负环
若以某个点为起点成功找到负环,那么我们可以减小二分答案 m i d mid mid,否则我们只能减小二分答案 m i d mid mid
二分最后的答案为 l l l,至于精度,只需精确到 1 0 − 10 10^{-10} 1010,即 1 e − 10 1e-10 1e10即可通过本题
理论最坏时间复杂度比较高,但可以通过本题,而且也只用了 105 m s 105ms 105ms,因为 S P F A SPFA SPFA的理论最优时间复杂度为 O ( m ) O(m) O(m)
其实,判负环有一种很好的方法,但很复杂,这里就不讲了
【如何判断负环】:

bool spfa(int u){
	vis[u]=true;int i;
	for(i=h[u];i;i=e[i].next){
		register int to=e[i].to;
		if (d[to]>d[u]+e[i].len){
			d[to]=d[u]+e[i].len;
			if (vis[to]) return true;
			else if (spfa(to,mid)) return true;
		}
	}
	vis[u]=false;
	return false;
}
//spfa(u)==true表示以u为起点有负环
//spfa(u)==false表示以u为起点无负环
//d数组初始化为0,vis数组初始化为false

【如何实数二分】:

	l=minn;r=maxn;//l,r的初始值
	while (l+eps<r){
		mid=(l+r)/2;
		if (check(mid)){
//			ans=mid;
			r=mid;//这里随题目要求不同而不同
		}
		else l=mid;//同上
	}

【代码】:

//By HPXXZYY,Accepted,100 points,105ms,1.13MB,1.25KB
#include 
using namespace std;
const int N=10100,M=3010;
struct node{
	int next,to;
	double len;
}e[N+M];int h[M],tot,n,m;
inline void add(int a,int b,double c){
	e[++tot]=(node){h[a],b,c};h[a]=tot;
//	e[++tot]=(node){h[b],a,c};h[b]=tot;//若为无向图,则加上此句
}
double d[M];bool vis[M];
const double eps=1e-10;//随题目要求不同而不同的精度,一般题目要求k位,就精确到k+2位即可
bool spfa(int u,double mid){
	vis[u]=true;int i;
	for(i=h[u];i;i=e[i].next){
		register int to=e[i].to;
		if (d[to]>d[u]+e[i].len-mid){
			d[to]=d[u]+e[i].len-mid;
			if (vis[to]) return true;
			else if (spfa(to,mid)) return true;
		}
	}
	vis[u]=false;
	return false;
}
int from[N],to[N];double len[N];
inline bool check(double mid){
	memset(d,0,sizeof(d));
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
	if (spfa(i,mid)) return true;
	return false;
}
double l,r,mid,ans,minn,maxn;
int main(){
//	freopen("t1.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	scanf("%d%d%lf",&from[i],&to[i],&len[i]);
	minn=1e20;maxn=-minn;
	for(int i=1;i<=m;i++){
		minn=min(minn,len[i]);
		maxn=max(maxn,len[i]);
	}
	for(int i=1;i<=m;i++)
	add(from[i],to[i],len[i]);
	l=minn;r=maxn;
	while (l+eps<r){
		mid=(l+r)/2;
		if (check(mid)){
//			ans=mid;
			r=mid;
		}
		else l=mid;
	}
	printf("%.8lf",l);
	return 0;
}

你可能感兴趣的:(原创)