DTOJ3303 W的火星工程

58 篇文章 0 订阅

题目

题目描述

大老板W的伟大工程扩大到了火星,他准备在火星建立一个自己的度假村
在他的度假村里,有两个大饭店A,B
对于W来说,修建度假村必不可少的就是从A饭店向B饭店修路,以保证他可以短时间内享受各种美味
火星上有一些中转站,中转站之间以及它们与饭店之间有路径使得能从一个到达另一个(路径为单向)
对于每一条路径,工程师ZQ给出了它的两个消费参数 a , b a,b a,b
W会从A饭店出发,经过其中的一些路径,最终到达B饭店
W希望他走过的所有道路的 ∑ a ∑ b \frac{\sum{a}}{\sum{b}} ba最小,也就是那些道路的 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的两个饭店AB
注意你并不需要把所有中转站都连入路中,只要保证从A饭店可以到达B饭店就行了

输出格式

输出一个小数,表示所求的最小值。误差不超过 1 0 − 6 10^{-6} 106即可

样例

样例输入

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,m100
对于 50 % 50 \% 50%的数据, n ⩽ 1000 n \leqslant 1000 n1000
对于 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 1n10000,1m100,000,0x,yn+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 aibi×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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值