题目
题目描述
大老板W
的伟大工程扩大到了火星,他准备在火星建立一个自己的度假村
在他的度假村里,有两个大饭店A
,B
对于W
来说,修建度假村必不可少的就是从A
饭店向B
饭店修路,以保证他可以短时间内享受各种美味
火星上有一些中转站,中转站之间以及它们与饭店之间有路径使得能从一个到达另一个(路径为单向)
对于每一条路径,工程师ZQ
给出了它的两个消费参数$a,b$W
会从A
饭店出发,经过其中的一些路径,最终到达B
饭店W
希望他走过的所有道路的$\frac{\sum{a}}{\sum{b}}$最小,也就是那些道路的$a$值之和除以他们的$b$值之和最小
可是路径实在太多了,W
不知道该如何选择
聪明的你需要帮助他计算出这个最小值
至于路径的选取方法你就不需要告诉他了
输入格式
输入两个整数$n,m$,表示中转站的数量和边的数量
随后$m$行,每行四个整数$x,y,a,b$,分别表示路径的两端,路径的$a,b$消费参数
其中$0$号点与$n+1$号点分别表示W
的两个饭店A
,B
注意你并不需要把所有中转站都连入路中,只要保证从A
饭店可以到达B
饭店就行了
输出格式
输出一个小数,表示所求的最小值。误差不超过$10^{-6}$即可
样例
样例输入
1 | 2 3 |
样例输出
1 | 0.400000 |
数据范围与提示
对于$20 \%$的数据,$n,m \leqslant 100$
对于$50 \%$的数据,$n \leqslant 1000$
对于$100 \%$的数据, $1 \leqslant n \leqslant 10000,1 \leqslant m \leqslant 100,000,0 \leqslant x,y \leqslant n+1$
数据保证$0$号饭店可以到达$n+1$号饭店,任意两个中转站或饭店间最多有一条边,且保证没有路可以构成环
题解
0/1分数规划裸题
考虑二分答案,记$dis_i$表示从0到i的路径$\sum a_i-b_i\times mid$的最小值,用类似最短路的方法更新,判断一下$dis_{n+1}$的值是否大于$0$,更改二分的范围
附上代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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);
}