acm-有向图中的最小平均权值回路

引言

对于求解有向图的最小均值环的问题一般由两种做法:【二分均值+spfa判定负环】【公式法】,本文将对两种做法进行推导,并提供相应的例题和变式训练。

本文以[Usaco2006 Mar]Milk Team Select产奶比赛为例题对两种方法进行介绍


法一:二分均值+spfa判定负环

原理详解:

设有向图中的所有回路的最小平均权值为ans,于是可以对ans进行二分处理,不过注意到当题目给出的条件中未明确要求环的权值非负的时候,ans可以是负数,这时候需要特殊处理。这里假设ans>=0,那么二分ans,每次对ans进行检查,检查的方式是先将所有边权都减去一个ans,再对改建后的图跑spfa,判定是否存在负环,如果没有负环,说明ans偏小,否则偏大。对于spfa而言可以采用dfs优化的spfa,能够优化运行速度,不过spfa最坏情况下仍然会被卡成 O ( ∣ V ∣ ∣ E ∣ ) O(|V||E|) O(VE)
总复杂度: O ( ∣ V ∣ ∣ E ∣ l o g ( 1 e 9 ) ) O(|V||E|log(1e9)) O(VElog(1e9))

代码详解:
后续待补充。。。

法二:公式法

Karp在1977年的论文,讲述了一种 O ( ∣ V ∣ ∣ E ∣ ) O(|V||E|) O(VE)的算法,算法的核心是一个公式: m i n v ∈ V { m a x 0 ≤ k ≤ n − 1 { F [ n ] [ v ] − F [ k ] [ v ] n − k } } = a n s \mathbf{min_{v\in V}\{max_{0\le k\le n-1}\{\frac{F[n][v]-F[k][v]}{n-k}\}\}=ans} minvV{max0kn1{nkF[n][v]F[k][v]}}=ans
其中 F [ k ] [ v ] \mathbf{F[k][v]} F[k][v]代表从s点出发经过k条边到达v点的最短距离(s点任意选取),适用条件为强联通图。
下面先给出Karp的原始论文,接着给出中文详细推导。

原论文:

Karp-proof-page1
Karp-proof-page2
Karp-proof-page3

个人详细推导:

前置定义:
1.设s为出发点,我们记 F [ k ] [ v ] \mathbf{F\lbrack k\rbrack\lbrack v\rbrack} F[k][v]为从s点出发经过k条边到达v点的最短距离。
2.设ans为所有回路的最小平均权值。
3.设 d i s t [ v ] \mathbf{dist\lbrack v\rbrack} dist[v]为从s点出发到达 v点的最小权值(暂时不考虑图中存在负环的情况)

现在从最简单的情形出发,进而推广到一般情
况。

考虑一张含有n个点的强联通图上存在边权和为0的环,后面称之为零环(注意零环不是指边权为0,而是边权和为0),且不存在负环,那么有如下两个结论:
1. a n s = 0 \mathbf{1.ans=0} 1.ans=0
2. d i s t [ v ] = m i n 0 ≤ k ≤ n − 1 { F [ k ] [ v ] } \mathbf{2.dist[v]=min_{0\le k\le n-1}\{F[k][v]\}} 2.dist[v]=min0kn1{F[k][v]}
结论1即假设,容易理解。
结论2可以用反正法,当 k ≥ n k\ge n kn的时候,从s点出发经过的边数大于n条边,意味着会经过n+1个点,大于全图的总点数。因此会有点被重复经过,也就形成了环,由于环非负,因此去掉该环所形成的路径长度一定会更优,因此 k < n k< n k<n一定成立。
对结论2的公式做如下变形:
F [ n ] [ v ] − d i s t [ v ] = F [ n ] [ v ] − m i n 0 ≤ k ≤ n − 1 { F [ k ] [ v ] } = m a x 0 ≤ k ≤ n − 1 { F [ n ] [ v ] − F [ k ] [ v ] } \mathbf{F\lbrack n\rbrack\lbrack v\rbrack-dist\lbrack v\rbrack=F\lbrack n\rbrack\lbrack v\rbrack-min_{0\le k\le n-1}\{F[k][v]\}=max_{0\le k\le n-1}\{F[n][v]-F[k][v]\}} F[n][v]dist[v]=F[n][v]min0kn1{F[k][v]}=max0kn1{F[n][v]F[k][v]}
由于 F [ n ] [ v ] ≥ d i s t [ v ] \mathbf{F[n][v]\ge dist[v]} F[n][v]dist[v],因此 m a x 0 ≤ k ≤ n − 1 { F [ n ] [ v ] − F [ k ] [ v ] } ≥ 0 \mathbf{max_{0\le k\le n-1}\{F[n][v]-F[k][v]\}\ge0} max0kn1{F[n][v]F[k][v]}0

接下来我将证明存在一个点v使得这个等于号一定
可以被取到

现在的目的是找到一个点v使得 F [ n ] [ v ] = d i s t [ v ] \mathbf{F[n][v]=dist[v]} F[n][v]=dist[v]
构造方法:由于图中存在零环,不妨从零环上任取一个点x,然后选定一条从s点到x的最短路径,沿着最短路径从s点走到x点,由前面的推导可知这条路径长度不会大于n-1,为了构造出n条边的路径,因此从x点出发,沿着这个零环继续走,直到走到一个点v使得s->x->v的路径长度恰好为n(见下图1)。图1
然后可以得到这样一个断言:通过这种构造方式得到的路径一定是从s点到v点的一条最短路。
下面通过反证法证明这个断言是正确的。
假设上述路径并不是从s点到v点的一条最短路径,那么必然存在另外一条更短的路径到达v点(见下图2)
图2
我们设这条更短的路径为s->y->v,即图中沿着紫色箭头走的路径。
下面约定 v a l 点 1 点 2 点 3.. 点 n \mathbf{val_{点1点2点3..点n}} val123..n 为沿着点1->点2->点3->…->点n的路径上的权值之和。
由于零环的边权之和为0,所以 v a l v g x + v a l v h x = 0 \mathbf{val_{vgx}+val_{vhx}=0} valvgx+valvhx=0
由于 v a l v g x \mathbf{val_{vgx}} valvgx v a l v h x \mathbf{val_{vhx}} valvhx地位对等,因此不妨设 v a l v g x ≤ 0 \mathbf{val_{vgx}\le 0} valvgx0
v a l v h x ≥ 0 \mathbf{val_{vhx}\ge 0} valvhx0,故
v a l s y v g x = v a l s y v + v a l v g x ≤ v a l s y v + 0 = v a l s y v ] . . . . . . . . . . . . . . . . . ( 1 ) \mathbf{val_{syvgx}= val_{syv}+val_{vgx}\le val_{syv}+0=val_{syv}]}.................(1) valsyvgx=valsyv+valvgxvalsyv+0=valsyv].................(1) v a l s x g v = d i s t [ x ] + v a l x g v = d i s t [ x ] + v a l v g x ≤ d i s t [ x ] + 0 = d i s t [ x ] . . . . . . . . . . . . . . . . . . . . . ( 2 ) \mathbf{val_{sxgv}=dist[x]+val_{xgv}=dist[x]+val_{vgx}\le dist[x]+0=dist[x]}.....................(2) valsxgv=dist[x]+valxgv=dist[x]+valvgxdist[x]+0=dist[x].....................(2)
由于假设s->y->v是一条比上文中构造法形成的路径更短的一条sv的路径,即假设 v a l s y v < v a l s x g v \mathbf{val_{syv}<val_{sxgv}} valsyv<valsxgv成立。
代入(1)(2)式后,也就转化为 v a l s y v g x ≤ v a l s y v < v a l s x g v ≤ d i s t [ x ] \mathbf{val_{syvgx}\le val_{syv}<val_{sxgv}\le dist[x]} valsyvgxvalsyv<valsxgvdist[x]
v a l s y v g x < d i s t [ x ] \mathbf{val_{syvgx}<dist[x]} valsyvgx<dist[x]
而由前文定义可知 d i s t [ x ] \mathbf{dist[x]} dist[x]代表从s点到x点的最短路径,现在发现一条s->y->v->g->x更短的路径,说明与定义矛盾!故初始假设错误,也就是说前面的构造法构造出来的s->x->v的路径一定是从s点到v点的最短路径,并且这条路径长度为n,也就是满足了该等式: F [ n ] [ v ] = d i s t [ v ] \mathbf{F[n][v]=dist[v]} F[n][v]=dist[v]
现在改写一下等式:
m i n v ∈ V { F [ n ] [ v ] − d i s t [ v ] } = 0 ⇒ \mathbf{min_{v\in V}\{F[n][v]-dist[v]\}=0\Rightarrow} minvV{F[n][v]dist[v]}=0
m i n v ∈ V { m a x 0 ≤ k ≤ n − 1 { F [ n ] [ v ] − F [ k ] [ v ] } } = 0 . . . . . . . . . . . . . . . . . . . . . . . ( 3 ) \mathbf{min_{v\in V}\{max_{0\le k\le n-1}\{F[n][v]-F[k][v]\}\}=0}.......................(3) minvV{max0kn1{F[n][v]F[k][v]}}=0.......................(3)
最后这个等式(3)就是这个算法的核心。
现在我们开始考虑更加一般的情形,假设是任意一张强联通图,我们考虑为每条边减去一个边权c(可以为负),使得整张图的所有环中权值和最小为0。在这种情况下(3)式一定成立,然后考虑再将图“变回去”,也就是对所有边权执行一个“加c”的操作。观察(3)式会做何变化:
F [ n ] [ v ] + = n ∗ c \mathbf{F[n][v]+=n*c} F[n][v]+=nc
F [ k ] [ v ] + = k ∗ c \mathbf{F[k][v]+=k*c} F[k][v]+=kc
(3)式的结果相应地会变成 ( n − k ) ∗ c \mathbf{(n-k)*c} (nk)c
如果将(3)式除以 ( n − k ) \mathbf{(n-k)} (nk),结果也就会变成c,我们可以得到如下等式:
m i n v ∈ V { m a x 0 ≤ k ≤ n − 1 { F [ n ] [ v ] − F [ k ] [ v ] n − k } } = c . . . . . . . . . . . . . . . . . . . . . . . ( 4 ) \mathbf{min_{v\in V}\{max_{0\le k\le n-1}\{\frac{F[n][v]-F[k][v]}{n-k}\}\}=c}.......................(4) minvV{max0kn1{nkF[n][v]F[k][v]}}=c.......................(4)
而这个c不正是原图中的最小平均权值吗,所以这个神奇的等式就被巧妙地推导出来了,(4)式即是我们想要并最终证明的公式。

代码详解:

要利用上述公式实现具体的算法,还需要得到预处理出 F [ k ] [ v ] \mathbf{F[k][v]} F[k][v]的值,利用dp的思想,很容易得到如下递推关系式:
F [ k ] [ v ] = m i n 边 u − > v 存 在 { F [ k − 1 ] [ u ] + w [ u ] [ v ] } \mathbf{F[k][v]=min_{边u->v存在}\{F[k-1][u]+w[u][v]\}} F[k][v]=minu>v{F[k1][u]+w[u][v]}
由于k最大更新到n,因此我们只需要 O ( ∣ V ∣ ∣ E ∣ ) \mathbf{O(|V||E|)} O(VE)的预处理即可。而
m i n v ∈ V { m a x 0 ≤ k ≤ n − 1 { F [ n ] [ v ] − F [ k ] [ v ] n − k } \mathbf{min_{v\in V}\{max_{0\le k\le n-1}\{\frac{F[n][v]-F[k][v]}{n-k}\}} minvV{max0kn1{nkF[n][v]F[k][v]}
该式子可以 O ( ∣ V ∣ 2 ) \mathbf{O(|V|^2)} O(V2)更新答案,于是总复杂度为 O ( ∣ V ∣ ( ∣ V ∣ + ∣ E ∣ ) ) \mathbf{O(|V|(|V|+|E|))} O(V(V+E)),对于稠密图而言复杂度约为 O ( ∣ V ∣ ∣ E ∣ ) \mathbf{O(|V||E|)} O(VE)

总复杂度: O ( ∣ V ∣ ∣ E ∣ ) \mathbf{O(|V||E|)} O(VE)

后续待补充。。。

推广:

例题一
题目来源:The 2019 China Collegiate Programming Contest Harbin Site(2019CCPC哈尔滨站)A题:Artful Paintings
题面:A题:Artful Paintings
官方题解:
推广例题一题解page1
推广例题一题解page2
(方法一实现代码):

采用二分边权+spfa判负环的思路。首先,设被涂黑的
格子个数为x,显然x越大越容易满足条件,于是二分x
的值,注意到有三种类型的边会包含x的边权:第二种	
限制条件下建的边(由r到l-1建边,边权为x-k)、由n到0
建边,边权为x、由0到n建边,边权为-x。二分x以后,
就从0点出发开始跑spfa看是否存在负环,注意到0
一定可以到达全图所有的点,经过所有的边。
不过本题需要注意的是要对spfa进行优化。

spfa优化一: 根据建图特点,我们可以发现任意一个非0节点都可以经过若干条边权为0的边到达0点,也就是说,从0点出发,如果到达某个非0节点时的距离为负数,那么再从该非0节点经过若干条0边权回到0节点,就一定能够形成一个负环。所以当发现某个节点v满足dist[v]<0的时候就可以得知图中存在负环,这时候可以停止跑spfa。
spfa优化二: 考虑SLF优化,SLF优化的具体内容是,对于每一个新入队的节点v,如果满足dist[v]<dist[队首节点],就可以将该节点提前加到队首,否则加到队尾。
spfa优化三: 考虑LLL优化,不过很遗憾,我尝试着在spfa中加入LLL优化,最终却取得了TLE的结果,因此本题中不适用LLL优化,不过具体原因是为什么,受限于我的能力还暂时不能给出解答。
不加优化时程序会TLE,如果加了优化一就可以跑出140ms的好成绩,加上优化二可以跑出31ms的好成绩。

#include <bits/stdc++.h>
#define FOR(i,a,b) for(register int i=(a);i<(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define pi pair<int,int>
#define mk(a,b) make_pair(a,b)
#define mygc(c) (c)=getchar()
#define mypc(c) putchar(c)
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 3005;
const int maxm = 30000;
const int inf = 2147483647;
typedef long long ll;
const double eps = 1e-9;
const long long INF = 9223372036854775807ll;
ll qpow(ll a,ll b,ll c){ll ans=1;while(b){if(b&1)ans=ans*a%c;a=a*a%c;b>>=1;}return ans;}
inline void rd(int *x){int k,m=0;*x=0;for(;;){mygc(k);if(k=='-'){m=1;break;}if('0'<=k&&k<='9'){*x=k-'0';break;}}for(;;){mygc(k);if(k<'0'||k>'9')break;*x=(*x)*10+k-'0';}if(m)(*x)=-(*x);}
inline void rd(ll *x){int k,m=0;*x=0;for(;;){mygc(k);if(k=='-'){m=1;break;}if('0'<=k&&k<='9'){*x=k-'0';break;}}for(;;){mygc(k);if(k<'0'||k>'9')break;*x=(*x)*10+k-'0';}if(m)(*x)=-(*x);}
inline void rd(db *x){scanf("%lf",x);}
inline int rd(char c[]){int i,s=0;for(;;){mygc(i);if(i!=' '&&i!='\n'&&i!='\r'&&i!='\t'&&i!=EOF) break;}c[s++]=i;for(;;){mygc(i);if(i==' '||i=='\n'||i=='\r'||i=='\t'||i==EOF) break;c[s++]=i;}c[s]='\0';return s;}
inline void rd(int a[],int n){FOR(i,0,n)rd(&a[i]);}
inline void rd(ll a[],int n){FOR(i,0,n)rd(&a[i]);}
template <class T, class S> inline void rd(T *x, S *y){rd(x);rd(y);}
template <class T, class S, class U> inline void rd(T *x, S *y, U *z){rd(x);rd(y);rd(z);}
template <class T, class S, class U, class V> inline void rd(T *x, S *y, U *z, V *w){rd(x);rd(y);rd(z);rd(w);}
inline void wr(int x){if(x < 10) putchar('0' + x); else wr(x / 10), wr(x % 10);}
inline void wr(int x, char c){int s=0,m=0;char f[10];if(x<0)m=1,x=-x;while(x)f[s++]=x%10,x/=10;if(!s)f[s++]=0;if(m)mypc('-');while(s--)mypc(f[s]+'0');mypc(c);}
inline void wr(ll x, char c){int s=0,m=0;char f[20];if(x<0)m=1,x=-x;while(x)f[s++]=x%10,x/=10;if(!s)f[s++]=0;if(m)mypc('-');while(s--)mypc(f[s]+'0');mypc(c);}
inline void wr(db x, char c){printf("%.15f",x);mypc(c);}
inline void wr(const char c[]){int i;for(i=0;c[i]!='\0';i++)mypc(c[i]);}
inline void wr(const char x[], char c){int i;for(i=0;x[i]!='\0';i++)mypc(x[i]);mypc(c);}
template<class T> inline void wrn(T x){wr(x,'\n');}
template<class T, class S> inline void wrn(T x, S y){wr(x,' ');wr(y,'\n');}
template<class T, class S, class U> inline void wrn(T x, S y, U z){wr(x,' ');wr(y,' ');wr(z,'\n');}
template<class T> inline void wra(T x[], int n){int i;if(!n){mypc('\n');return;}FOR(i,0,n-1)wr(x[i],' ');wr(x[n-1],'\n');}
int n,ee=0,h[maxn],x,vis[maxn],d[maxn],cnt[maxn];
struct Edge{
	int v,tp,w,next;
}e[maxm];
void addedge(int u,int v,int w,int tp){
	e[ee]=Edge{v,tp,w,h[u]};
	h[u]=ee++;
}
bool spfa(){
	deque<int>q;
	memset(vis,0,sizeof(int)*(n+1));
	memset(d,0x3f,sizeof(int)*(n+1));
	memset(cnt,0,sizeof(int)*(n+1));
	d[0]=0;
	cnt[0]=1;
	q.push_front(0);
	vis[0]=1;
	while(!q.empty()){
		int u=q.front();q.pop_front();
		vis[u]=0;
		for(register int i=h[u];i!=-1;i=e[i].next){
			int v=e[i].v,w=e[i].w+e[i].tp*x;
			if(d[v]>d[u]+w){
				d[v]=d[u]+w;
				if(d[v]<0)return 1;//根据建图特点优化:TLE->140ms 
				if(vis[v])continue;
				cnt[v]++;
				if(cnt[v]>n)return 1;
				if(!q.empty() && d[v]<d[q.front()])q.push_front(v);//SLF优化:140ms->31ms 
				else q.push_back(v);
				vis[v]=1;
			}
		}
	}
	return 0;
}
int main(){
	int t;rd(&t);
	while(t--){
		int m1,m2;
		rd(&n,&m1,&m2);
		ee=0;
		memset(h,-1,sizeof(int)*(n+1));
		FOR(i,1,n+1)addedge(i-1,i,1,0),addedge(i,i-1,0,0);
		while(m1--){
			int l,r,k;
			rd(&l,&r,&k);
			addedge(r,l-1,-k,0);
		} 
		while(m2--){
			int l,r,k;
			rd(&l,&r,&k);
			addedge(l-1,r,-k,1);
		}
		addedge(0,n,0,1);
		addedge(n,0,0,-1);
		int l=0,r=n,ans=-1;
		while(l<=r){
			int mid=l+r>>1;
			x=mid;
			bool res=spfa();
			if(res)l=mid+1;
			else{
				r=mid-1;
				ans=x;
			}
		}
		printf("%d\n",ans);
	} 
}

(方法二实现代码):

与上述官方题解不相同,由于作者能力有限,未能
实现官方题解中g[i][j]的更新过程,因此在本代码中
将采用队列来实现对边的松弛操作,导致复杂度不优
,无法通过本题的测评,会超时,也希望高手能够
提供更为优秀的解法。本题个人推荐采用方法一实现。
\\运用方法二(公式法实现),实测按照传统更新方式无法通过本题,推荐采用方法一
#include <bits/stdc++.h>
#define FOR(i,a,b) for(register int i=(a);i<(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define pi pair<int,int>
#define mk(a,b) make_pair(a,b)
#define mygc(c) (c)=getchar()
#define mypc(c) putchar(c)
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 3005;
const int maxm = 20000;
const int inf = 2147483647;
typedef long long ll;
const double eps = 1e-9;
const long long INF = 9223372036854775807ll;
ll qpow(ll a,ll b,ll c){ll ans=1;while(b){if(b&1)ans=ans*a%c;a=a*a%c;b>>=1;}return ans;}
inline void rd(int *x){int k,m=0;*x=0;for(;;){mygc(k);if(k=='-'){m=1;break;}if('0'<=k&&k<='9'){*x=k-'0';break;}}for(;;){mygc(k);if(k<'0'||k>'9')break;*x=(*x)*10+k-'0';}if(m)(*x)=-(*x);}
inline void rd(ll *x){int k,m=0;*x=0;for(;;){mygc(k);if(k=='-'){m=1;break;}if('0'<=k&&k<='9'){*x=k-'0';break;}}for(;;){mygc(k);if(k<'0'||k>'9')break;*x=(*x)*10+k-'0';}if(m)(*x)=-(*x);}
inline void rd(db *x){scanf("%lf",x);}
inline int rd(char c[]){int i,s=0;for(;;){mygc(i);if(i!=' '&&i!='\n'&&i!='\r'&&i!='\t'&&i!=EOF) break;}c[s++]=i;for(;;){mygc(i);if(i==' '||i=='\n'||i=='\r'||i=='\t'||i==EOF) break;c[s++]=i;}c[s]='\0';return s;}
inline void rd(int a[],int n){FOR(i,0,n)rd(&a[i]);}
inline void rd(ll a[],int n){FOR(i,0,n)rd(&a[i]);}
template <class T, class S> inline void rd(T *x, S *y){rd(x);rd(y);}
template <class T, class S, class U> inline void rd(T *x, S *y, U *z){rd(x);rd(y);rd(z);}
template <class T, class S, class U, class V> inline void rd(T *x, S *y, U *z, V *w){rd(x);rd(y);rd(z);rd(w);}
inline void wr(int x){if(x < 10) putchar('0' + x); else wr(x / 10), wr(x % 10);}
inline void wr(int x, char c){int s=0,m=0;char f[10];if(x<0)m=1,x=-x;while(x)f[s++]=x%10,x/=10;if(!s)f[s++]=0;if(m)mypc('-');while(s--)mypc(f[s]+'0');mypc(c);}
inline void wr(ll x, char c){int s=0,m=0;char f[20];if(x<0)m=1,x=-x;while(x)f[s++]=x%10,x/=10;if(!s)f[s++]=0;if(m)mypc('-');while(s--)mypc(f[s]+'0');mypc(c);}
inline void wr(db x, char c){printf("%.15f",x);mypc(c);}
inline void wr(const char c[]){int i;for(i=0;c[i]!='\0';i++)mypc(c[i]);}
inline void wr(const char x[], char c){int i;for(i=0;x[i]!='\0';i++)mypc(x[i]);mypc(c);}
template<class T> inline void wrn(T x){wr(x,'\n');}
template<class T, class S> inline void wrn(T x, S y){wr(x,' ');wr(y,'\n');}
template<class T, class S, class U> inline void wrn(T x, S y, U z){wr(x,' ');wr(y,' ');wr(z,'\n');}
template<class T> inline void wra(T x[], int n){int i;if(!n){mypc('\n');return;}FOR(i,0,n-1)wr(x[i],' ');wr(x[n-1],'\n');}
int n,ee,g[maxn][maxn],h[maxn],vis[2][maxn];//vis主要是一点小优化,防止重复入队 
struct Edge{
	int v,w1,w2,next;
}e[maxm];
void addedge(int u,int v,int w1,int w2){
	e[ee]=Edge{v,w1,w2,h[u]};
	h[u]=ee++;
}
queue<int>q[2];
db max(db a,db b){
	if(a>b)return a;
	return b;
}
db min(db a,db b){
	if(a>b)return b;
	return a;
}
int main(){
	int t;rd(&t);
	while(t--){
		int m1,m2;
		rd(&n,&m1,&m2);
		memset(h,-1,sizeof(int)*(n+1));
		ee=0;
		FOR(i,0,n)addedge(i,i+1,0,1),addedge(i+1,i,0,0);
		while(m1--){
			int l,r,k;
			rd(&l,&r,&k);
			addedge(r,l-1,0,-k);
		}
		while(m2--){
			int l,r,k;
			rd(&l,&r,&k);
			addedge(l-1,r,1,-k);
		}
		addedge(0,n,1,0);
		int o=0;
		FOR(i,0,n+2)
		FOR(j,0,n+1)g[i][j]=inf/4;
		g[0][0]=0;q[o].push(0);
		FOR(i,0,n+2){
			o=i%2;
			while(!q[o].empty()){
				int u=q[o].front();q[o].pop();
				vis[o][u]=0;
				for(register int j=h[u];j!=-1;j=e[j].next){
					int v=e[j].v,w1=e[j].w1,w2=e[j].w2;
					if(i<n+1 && w1 && g[i+1][v]>g[i][u]+w2){//第一种松弛操作,由g[i][u]更新g[i+1][v],满足u->v边为1*x+b类型 
						g[i+1][v]=g[i][u]+w2;
						if(!vis[o^1][v])q[o^1].push(v),vis[o^1][v]=1;
					}else if(!w1 && g[i][v]>g[i][u]+w2){//第二种松弛操作,由g[i][u]更新g[i][v],满足u->v边为0*x+b类型 
						g[i][v]=g[i][u]+w2;
						if(!vis[o][v])q[o].push(v),vis[o][v]=1;
					}
				}
			}
			wra(g[i],n+1);
		}
		db ans1=inf,ans2=inf;//由于ax+b中b/a可能是小数,所以采用double类型处理 
		FOR(i,0,n+1){
			db maxx=0;
			FOR(j,0,n+1)maxx=max(maxx,1.0*(g[n+1][i]-g[j][i])/(n+1-j));
			ans1=min(ans1,maxx);
		}
		FOR(i,2,n+2)ans2=min(ans2,1.0*g[i][n]/(i-1));
		db ans=-min(ans1,ans2);
		int as=0;
		if(fabs(ans-(int)ans)<eps)as=(int)(ans+0.5);
		else as=(int)ans+1;
		wrn(as);
	} 
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值