题目
题目描述
大老板W
的伟大工程扩大到了火星,他准备在火星建立一个自己的度假村
在他的度假村里,有两个大饭店A
,B
对于W
来说,修建度假村必不可少的就是从A
饭店向B
饭店修路,以保证他可以短时间内享受各种美味
火星上有一些中转站,中转站之间以及它们与饭店之间有路径使得能从一个到达另一个(路径为单向)
对于每一条路径,工程师ZQ
给出了它的两个消费参数
a
,
b
a,b
a,b
W
会从A
饭店出发,经过其中的一些路径,最终到达B
饭店
W
希望他走过的所有道路的
∑
a
∑
b
\frac{\sum{a}}{\sum{b}}
∑b∑a最小,也就是那些道路的
a
a
a值之和除以他们的
b
b
b值之和最小
可是路径实在太多了,W
不知道该如何选择
聪明的你需要帮助他计算出这个最小值
至于路径的选取方法你就不需要告诉他了
输入格式
输入两个整数
n
,
m
n,m
n,m,表示中转站的数量和边的数量
随后
m
m
m行,每行四个整数
x
,
y
,
a
,
b
x,y,a,b
x,y,a,b,分别表示路径的两端,路径的
a
,
b
a,b
a,b消费参数
其中
0
0
0号点与
n
+
1
n+1
n+1号点分别表示W
的两个饭店A
,B
注意你并不需要把所有中转站都连入路中,只要保证从A
饭店可以到达B
饭店就行了
输出格式
输出一个小数,表示所求的最小值。误差不超过 1 0 − 6 10^{-6} 10−6即可
样例
样例输入
2 3
0 1 1 2
0 2 2 3
1 3 1 3
样例输出
0.400000
数据范围与提示
对于
20
%
20 \%
20%的数据,
n
,
m
⩽
100
n,m \leqslant 100
n,m⩽100
对于
50
%
50 \%
50%的数据,
n
⩽
1000
n \leqslant 1000
n⩽1000
对于
100
%
100 \%
100%的数据,
1
⩽
n
⩽
10000
,
1
⩽
m
⩽
100
,
000
,
0
⩽
x
,
y
⩽
n
+
1
1 \leqslant n \leqslant 10000,1 \leqslant m \leqslant 100,000,0 \leqslant x,y \leqslant n+1
1⩽n⩽10000,1⩽m⩽100,000,0⩽x,y⩽n+1
数据保证
0
0
0号饭店可以到达
n
+
1
n+1
n+1号饭店,任意两个中转站或饭店间最多有一条边,且保证没有路可以构成环
题解
0/1分数规划裸题
考虑二分答案,记
d
i
s
i
dis_i
disi表示从0到i的路径
∑
a
i
−
b
i
×
m
i
d
\sum a_i-b_i\times mid
∑ai−bi×mid的最小值,用类似最短路的方法更新,判断一下
d
i
s
n
+
1
dis_{n+1}
disn+1的值是否大于
0
0
0,更改二分的范围
附上代码:
#include<cstdio>
#include<queue>
using namespace std;
int n,m,tot,nxt[100010],head[10010],to[100010],a[100010],b[100010],v[100010];
double L,R=1e9,w[100010],dis[100010];
void add(int u,int v)
{
nxt[++tot]=head[u],head[u]=tot,to[tot]=v;
}
int pd(double x)
{
for(int i=0;i<n+2;i++) dis[i]=1e9,v[i]=0;
for(int i=1;i<=m;i++) w[i]=(double)a[i]-(double)b[i]*x;
queue<int> q;
dis[0]=0,q.push(0);
while(!q.empty()){
int x=q.front();
q.pop(),v[x]=0;
for(int i=head[x];i;i=nxt[i]) if(dis[to[i]]>dis[x]+w[i]){
dis[to[i]]=dis[x]+w[i];
if(!v[to[i]]) q.push(to[i]);
v[to[i]]=1;
}
}
return dis[n+1]>=0;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<=m;i++) scanf("%d%d%d%d",&u,&v,&a[i],&b[i]),add(u,v);
while(L+0.000000001<R){
double mid=(L+R)/2;
if(pd(mid)) L=mid;
else R=mid;
}
printf("%.6lf",L);
}