D-Link with Game Glitch
题目
https://ac.nowcoder.com/acm/contest/33187/D
算法
①二分答案+SPFA判负环
②Karp的最小平均权重环路算法
题解
这里考虑法②
题目有对于配方i有: 以ai个物品 bi为原料,制造ci个物品di为产品
ai×bi→ci×di
可以理解为对于配方i有: 以ciai个物品 bi为原料,制造1个物品di为产品
ciai×bi→di
对于连续k个配方可以由配方的系数乘积从原料制造出产品
i∏kciai原料→产品
若某物品经过k道配方, 最终可以制造自己, 称为一循环配方
i∏kciai物品→物品
以n种物品为点, m种配方为边, ciai为边权建立有向图, 循环配方表现为环
设循环配方中任意物品原有数量为x,个 则每次循环后可变为为∏ikciaix个,
要使物品不会变多, 则必须x≥∏ikciaix, 也即
i∏kciai≥1
引入生产修正参数w
ai×bi→w×ci×di
可以理解为
wciai×bi→di
可推得
i∏kwciai物品→物品
将w提出有
wk1i∏kciai物品→物品
重复上文推论
以n种物品为点, m种配方为边, wciai为边权建立有向图, 循环配方表现为环
设循环配方中任意物品原有数量为x,个 则每次循环后可变为为wk1∏ikciaix个,
要使物品不会变多, 则必须x≥wk1∏ikciaix, 也即
i∏kciai≥wk
对于不同的环有不同的∏ikciai, 即要求在每一个环中都满足上式,即
min(i∏kciai)≥wk
且要求w取最大, 则要求
min(i∏kciai)=wk
化简得
w=min⎝⎛ki∏kciai⎠⎞
其中连乘可用对数处理
ln(i∏kciai)=i∑kln(ciai)
化简得
i∏kciai=e∑ikln(ciai)
即要求
wk=emin(∑ikln(ciai))
最终
w=min(ke∑ikln(ciai))
w=exp(min(k∑ikln(ciai)))
则理解为求环的最小平均权重
以ln(ciai)为边权建图G(V,E)
有Karp的最小平均权重环路算法
让F[k][v]为从任意点出发,经过k条边到达v点的最短距离, 对每条边u→v 有
F[k][v]=e∈Emin{F[k−1][u]+w[u][v]}
ans=v∈Vmin{0≤k≤n−1max{n−kF[n][v]−F[k][v]}}
详细请参考***********************************************************
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2005;
int n,m,b,d;
double a,c,ans;
double dp[maxn][maxn];
struct edge {
int u;
int v;
double w;
};
vector<edge> edges;
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a>>b>>c>>d;
edges.push_back({b,d,log(a/c)});
}
for (int i=1;i<=n;i++) {
for (auto [u,v,w] : edges) {
dp[i][v] = min(dp[i][v],dp[i-1][u]+w);
}
}
for(int i=1;i<=n;i++){
double t = -INFINITY;
for(int j=0;j<n;j++){
double dis = (dp[n][i]-dp[j][i]);
int k = (n-j);
t = max(t,dis/k);
}
ans = min(t,ans);
}
printf("%.15lf\n",exp(ans));
return 0;
}