葫芦
描述
Tom 最喜欢的歌曲就是《葫芦娃》。
一日表演唱歌,他尽了洪荒之力,唱响心中圣歌。
随之,Tom 进入了葫芦世界。
葫芦世界有 n 个葫芦,标号为 1~ n。n 个葫芦由 m 条藤连接,每条藤连接了两个葫芦, 这些藤构成了一张有向无环图。Tom 爬过每条藤都会消耗一定的能量。
Tom 站在 1 号葫芦上(你可以认为葫芦非常大,可以承受 Tom 的体重),他想沿着藤爬 到 n 号葫芦上,其中每个葫芦只经过一次。
Tom 找到一条路径,使得消耗的能量与经过的葫芦数的比值最小。
输入
输入文件第一行两个正整数 n,m,分别表示葫芦的个数和藤数。
接下来 m 行,每行三个正整数 u,v,w,描述一条藤,表示这条藤由 u 连向 v,Tom 爬过 这条藤需要消耗 w 点能量。
输出
一行一个实数,表示答案(误差不超过 10^-3)。
样例输入
4 6
1 2 1
2 4 6
1 3 2
3 4 4
2 3 3
1 4 8
样例输出
2.000
提示
有 4 种爬法:
1->4,消耗能量 8,经过 2 个葫芦,比值为 8/2=4。
1->2->4,消耗能量 1+6=7,经过 3 个葫芦,比值为 7/3≈2.33。
1->3->4,消耗能量 2+4=6,经过 3 个葫芦,比值为 6/3=2。
1->2->3->4,消耗能量 1+3+4=8,经过 4 个葫芦,比值为 8/4=2。
所以选第三种或第四种方案,答案为 2。
测试点编号 | n | m | 特殊说明 |
---|---|---|---|
1 | 2 | 1 | |
2 | 100 | 99 | 除 1 外,所有葫芦的入度均为 1 |
3 | 100 | 105 | 所有从 1 到 n 的路径经过的葫芦数相等 |
4 | 100 | 1000 | |
5 | 100 | 1000 | |
6 | 199 | 198 | 除 1 外,所有葫芦的入度均为 1 |
7 | 200 | 231 | 所有从 1 到 n 的路径经过的葫芦数相等 |
8 | 200 | 2000 | |
9 | 200 | 2000 | |
10 | 200 | 2000 |
对于所有数据,Tom 爬过每条藤消耗的能量不会超过 10^3,且一定存在一条从 1 到 n 的路径。
分析
水题*3
很明显啊,一眼分数规划
但是笨笨的我一不小心推错了
看到分数规划,首先想到sigma(ai)/sigma(bi)这个式子
然后将这道题具体的 ai 和 bi 代入进去,得到
令
相当于现在就是要求 x ,我们就二分求解,问题就在于如何check了,再转化一下:
现在来二分,得到一个mid,如果
那么显然mid还可以调大一些,同理如果就说明mid太大了
check的主要部分完了,我们又该怎么判这个关系呢?
根据题意我们需要最小值,那就新建一个图在原图的基础上将边权改为,跑一遍最短路就好啦
代码
#include<bits/stdc++.h>
#define ll long long
#define in read()
#define N 205
#define M 4005
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
char ch;int f=1,res=0;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
while(ch>='0'&&ch<='9'){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
return f==1?res:-res;
}
int n,m;
int nxt[M],to[M],head[N],cnt=0,from[N];
double w[M];
void add(int x,int y,int z){
nxt[++cnt]=head[x];head[x]=cnt;
to[cnt]=y;w[cnt]=z;
}
double wne[M],d[N];
bool vis[N];
void spfa(){
memset(d,127,sizeof(d));
queue<int > q;q.push(1);vis[1]=1;d[1]=0;
while(!q.empty()){
int u=q.front();q.pop();vis[u]=0;
for(int e=head[u];e;e=nxt[e])
{
int v=to[e];
if(d[v]>d[u]+wne[e]){
d[v]=d[u]+wne[e];
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
}
bool check(double l){
for(int i=1;i<=cnt;++i) wne[i]=w[i]-l;
spfa();
if(d[n]>l) return true;
return false;
}
int main(){
n=in;m=in;int flag=0;
for(int i=1;i<=m;++i){
int u,v,w;
u=in;v=in;w=in;
add(u,v,w);
}
double l=0,r=3000000;
while(l+1e-10<r){
double mid=(l+r)/2.0;
if(check(mid)) l=mid;
else r=mid;注意实数的二分,不存在加1减1
}
printf("%lf",l);
return 0;
}
memset(128)-->极小值
memset(127)->极大值