NOIP模拟 葫芦(分数规划)

【题目描述】

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 的路径。

【题目分析】

另建一个起点 0,连接一条 0 到 1 长度为 0 的边,就此将问题转化为长度和 边数最小比值。

这个问题的求解需要分数规划。 假设答案为 ans,对于任意一条由 k 条边组成的路径,有: (w1+w2+w3+…+wk)/k>=ans; 转化一下: (w1+w2+w3+…+wk) >=ans*k; 即(w1-ans)+(w2-ans)+(w3-ans)+…+(wk-ans)>=0。 于是就得到了这样一个算法: 二分答案 x,每次将每一条边的权值减去 x 求最短路,判断 1~n 的最短路是 否大于 0:若大于 0,则说明答案 ans>x;否则说明 ans<x。

【代码~】

#include<bits/stdc++.h>
#define eps 0.0001
using namespace std;
const int N=40000,M=60600;
int tot,que[N],a,b,nxt[M],head[N],vis[M],n,m,dis[M],c;
double pre[M],to[M];
bool edge[N];
void addedge(int a,int b,int c)
{
	++tot;
	nxt[tot]=head[a];
	head[a]=tot;
	vis[tot]=b;
	dis[tot]=c;
	return ;
}
bool spfa(double v)
{
	for(int i=2;i<=n;i++)
		to[i]=1999999999;
	to[1]=0;
	for(int i=1;i<=tot;i++)
		pre[i]=dis[i]-v;
	int he=1,tail=1;
	que[he]=1;
	edge[1]=true;
	while(he<=tail)
	{
		int u=que[he];
		edge[u]=false;
		for(int p=head[u],v=vis[p];p;p=nxt[p],v=vis[p])
		{
			if(to[v]-eps>to[u]+pre[p])
			{
				to[v]=to[u]+pre[p];
				if(!edge[v])
				{
					que[++tail]=v;
					edge[v]=true;
				}
			}
		}
		he++;
	}
	return v-to[n]>eps;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;++i)
	{
		cin>>a>>b>>c;
		addedge(a,b,c);
	}
	double mid,l=0,r=1e3;
	while((r-l)>eps)
	{
		mid=(l+r)/2.0;
		if(spfa(mid))
			r=mid;
		else l=mid;
	}
	printf("%0.3lf",l);
	return 0;
}

 

posted @ 2018-10-11 14:21  Ishtar~  阅读(201)  评论(0编辑  收藏  举报