F - Beautiful Path (atcoder.jp)
分析:给了我们一个图,图中每条边有一个美观度和成本,要求我们找到从点1到点n的最大贡献(贡献为sum路径上美观度/sum路径上成本和)
01分数规划很好解决这个问题(求最优比率)
答案的公式为
(以下公式中省略了max)
转化一下变成
如何求解这样的式子?
二分答案,把边替换成,求出1-n的最短路(为什么是求最短路?下文揭晓),此时
为什么是最短路呢?而不是最长路?
因为dist[n]如果小于等于0,就说
,此时ans一定存在一个更大的解 使得
而我们就是希望求得一个最大的ans,所以我们求n的最短路使得式子的值尽可能小
dist[n]<=0说明ans的最大解一定大于等于当前的尝试的比率,反而则一定小于等于(由于是小数二分记得限定二分次数)
二分的判断条件 check()具体实现:由于所有的边都是从小的点连向大的点,并且一定存在一条1-n的路径,符合拓扑序
我们可以尝试把边替换成,然后通过dp求出1-n的最短路。
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010,M=2*N;
int h[N], e[M], w1[M],w2[M], ne[M], idx;
double q[N], dist[N];
bool st[N];
void add(int a, int b, int t1 ,int t2)
{
e[idx] = b, w1[idx] = t1,w2[idx] = t2, ne[idx] = h[a], h[a] = idx ++ ;
}
int n, m;
bool check(double mid){
for(int i=1;i<=n;i++) dist[i]=1e18;
dist[1]=0;
for(int i=1;i<=n;i++){
for(int j=h[i];~j;j=ne[j]){
int k=e[j];
dist[k]=min(dist[k],dist[i]+mid*w2[j]-w1[j]);
}
}
return dist[n]<=0;
}
signed main() {
cin >> n >> m;
memset(h,-1,sizeof h);
for (int i = 0; i < m; i++) {
int u, v, b, c;
std::cin >> u >> v >> b >> c;
add(u,v,b,c);
}
double l = 0, r = 1e9;
for (int t = 0; t <= 100; t++) {
double mid = (l + r) / 2;
if (check(mid)) {
l = mid;
} else {
r = mid;
}
}
printf("%.12lf",l);
}