水题你们是不是看够了?
很好,拔剑吧!,接下来是个小BOSS,泥泞的道路。
题目描述 Description
CS有n个小区,并且任意小区之间都有两条单向道路(a到b,b到a)相连。因为最近下了很多暴雨,很多道路都被淹了,不同的道路泥泞程度不同。小A经过对近期天气和地形的科学分析,绘出了每条道路能顺利通过的时间以及这条路的长度。
现在小A在小区1,他希望能够很顺利地到达目的地小区n,请帮助小明找出一条从小区1出发到达小区n的所有路线中“(总路程/总时间)“ 最大的路线。请你告诉他这个值。
输入描述 Input Description
第一行包含一个整数n,为小区数。
接下来n*n的矩阵P,其中第i行第j个数表示从小区i到小区j的道路长度为Pi,j。第i行第i个数的元素为0,其余保证为正整数。
接下来n*n的矩阵T,第i行第j个数表示从小区i到小区j需要的时间Ti,j。第i行第i个数的元素为0,其余保证为正整数。
输出描述 Output Description
写入一个实数S,为小区1到达n的最大答案,S精确到小数点后3位。
样例输入 Sample Input
3
0 8 7
9 0 10
5 7 0
0 7 6
6 0 6
6 2 0
样例输出 Sample Output
2.125
数据范围及提示 Data Size & Hint
【数据说明】
30%的数据,n<=20
100%的数据,n<=100,p,t<=10000
这题咋一看非常的迷….根本无从下手啊,是从路程开始二分,还是从时间开始二分呢?
答案是:都不需要。
接下来就要看你们的数学能力了。
请帮助小明找出一条从小区1出发到达小区n的所有路线中“(总路程/总时间)“ 最大的路线。
设路程数组为dis(区别于下面的dist!),时间为(tim),
设一个数组x,x[i]=1表示第i条边在所选的路线中,x[i]=0表示第i条边不在路线中,(所以,dis[i],tim[i],为定值,x[i]不是定值,x[i]会随着你跑spfa而变,下面会提到)
很好,假设 我们现在已经知道答案了,就是
R=(
∑i=1ndis[i]∗x[i]
)/(
∑i=1ntim[i]∗x[i]
)
∑i=1ndis[i]∗x[i]
就是我们的最优解情况下总路程
∑i=1ntim[i]∗x[i]
就是我们的最优解情况下总时间
处理一下就是:
R*(
∑i=1ntim[i]∗x[i]
)=(
∑i=1ndis[i]∗x[i]
)
那么(
∑i=1ndis[i]∗x[i]
)-R*(
∑i=1ntim[i]∗x[i]
)=0
↑在下面会提到
在这里R是我们的”最终解“!
在这里R是我们的”最终解“!
在这里R是我们的”最终解“!
重要的话说三遍。
定义一个函数f(L)=(
∑i=1ndis[i]∗x[i]
)-L*(
∑i=1ntim[i]∗x[i]
)
我们需要让f(L)不断接近R,直到f(L)==R
化简一下就是f(L)=(
∑i=1nx[i]∗(dis[i]−L∗tim[i])
)
设d[i]=dis[i]-L*tim[i];
则f(L)=(
∑i=1nx[i]∗d[i]
)
此时的d数组就相当于我们每条边的权值。
我们可以这样想:当前的f(L)相当于当前解,因为它是跑spfa过来的,所以选取了其中的一些边(最优边),因为是从节点1跑到节点n,那么dist[n]==(
∑i=1nx[i]∗d[i]
)。)==f(L)(区别于上面的dis!,这里的dist是跑spfa时记录最短路的数组(本题中为最长路)
巧妙的把这个题转化成了最长路问题
请帮助小明找出一条从小区1出发到达小区n的所有路线中“(总路程/总时间)“ 最大的路线。
↑所以这题是跑最长路,该是能看的懂吧(跑最长路的过程中已经把不存在于代码中的x[i]的值求出来了),
当f(L)==0时,(看上面)L就是我们要求的R,就是最优解。
当f(L)>0时,我们推一下(这个地方有点绕,请认真思考,反正我一开始是一脸懵逼),
f(L)=(
∑i=1ndis[i]∗x[i]
)-L*(
∑i=1ntim[i]∗x[i]
)且f(L)>0,
所以(
∑i=1ndis[i]∗x[i]
)>L*(
∑i=1ntim[i]∗x[i]
)
所以(
∑i=1ndis[i]∗x[i]
)/(
∑i=1ntim[i]∗x[i]
)>L,
说明,对于一个确定的参数L来说,如果一个方案使得f(L)>0那么这组方案可以得到一个比现在的L更优的一个L,
L如果增大的话,得到的解更优,L需要增大对吧!
那么什么时候f(L)>0呢?
有两种情况:
1、dist[n]>0
上面已经说了,dist[n]就相当于L,所以dist[n]>0时,L就大于0咯。
2、存在正环
因为本题是跑最长路嘛,如果你找到一个正环,在里面绕个几亿亿亿遍,当前的权值肯定是非常大的,还怕跑到节点n时dist[n]小于零?
对于这个题来说…..二分倒不是那么难打了,难就难在上面的推公式上。
db div()
{
db l = 0,r = 100000000;
db ans=0;
int k = 0;
while(k <= 100)
{
double mid = ((l + r) / (double)(2));
if(!spfa(mid))
r = mid;
else
l = mid,ans=max(ans,mid);
k ++;
// printf("l:%.1lf r:%.1lf\n",l,r);
}
return ans;
}
浮点数二分部分….和平常的整数二分区别很大,关于里面的k<=100,我只想说:
2^100=
本题这么小的数据,还怕分不完?
如果精度大的话,把100往上提就是了,有个科学家说过,如果能把全宇宙的粒子按质量从小到大排序,你如果想找宇宙中的任何一个粒子,使用二分查找,只七百多次就足够了。
还怕耗不过你的数据?
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
typedef double db;
const int maxn=20000;
const db inf=0x7ffffff;
struct Edge
{
int f;
int to;
db d;
db tim;
int next;
}edge[maxn];
int head[maxn],time[maxn];
db dist[maxn];
bool vis[maxn];
int n;
db dis[150][150],tim[150][150];
int tot;
void add(int f,int t,db d,db tt)
{
edge[++tot].to=t;
edge[tot].d=d;
edge[tot].tim=tt;
edge[tot].next=head[f];
head[f]=tot;
}
deque<int>q;
void clr()
{
tot=0;
while(!q.empty())
q.pop_front();
for(int i=1;i<=n;i++)
dist[i]=(-1)*inf;
memset(vis,0,sizeof(vis));
memset(time,0,sizeof(time));
// printf("%lf\n",dist[1]);
}
bool spfa(db mid)
{
clr();
q.push_front(1);
dist[1]=0.0;
while(!q.empty())
{
int x=q.front();
q.pop_front();
vis[x]=0;
for(int i=head[x];i;i=edge[i].next)
{
Edge e=edge[i];
if(dist[e.to]<dist[x]+(e.d-mid*e.tim))
{
dist[e.to]=dist[x]+(e.d-mid*e.tim);
if(!vis[e.to])
{
vis[e.to]=true;
time[e.to]++;
if(time[e.to]>n)
{
return true;
}
if(!q.empty()&&dist[e.to]>dist[q.front()])
q.push_front(e.to);
else
q.push_back(e.to);
}
}
}
}
if(dist[n]>0)
return true;
return false;
}
db div()
{
db l = 0,r = 100000000;
db ans=0;
int k = 0;
while(k <= 100)
{
double mid = ((l + r) / (double)(2));
if(!spfa(mid))
r = mid;
else
l = mid,ans=max(ans,mid);
k ++;
// printf("l:%.1lf r:%.1lf\n",l,r);
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%lf",&dis[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%lf",&tim[i][j]);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i!=j)
{
add(i,j,dis[i][j],tim[i][j]);
}
}
}
printf("%.3f",div());
return 0;
}