D-Link with Game Glitch

题目

https://ac.nowcoder.com/acm/contest/33187/D

算法

①二分答案+SPFA判负环

②Karp的最小平均权重环路算法

题解

这里考虑法②

题目有对于配方ii有: 以aia_i个物品 bib_i为原料,制造cic_i个物品did_i为产品

ai×bici×dia_i\times b_i\to c_i \times d_i

可以理解为对于配方ii有: 以aici\frac{a_i}{c_i}个物品 bib_i为原料,制造11个物品did_i为产品

aici×bidi\frac{a_i}{c_i}\times b_i \to d_i

对于连续kk个配方可以由配方的系数乘积从原料制造出产品

ikaici\prod_{i}^k\frac{a_i}{c_i} 原料\to 产品

若某物品经过kk道配方, 最终可以制造自己, 称为一循环配方

ikaici\prod_{i}^k\frac{a_i}{c_i} 物品\to 物品

nn种物品为点, mm种配方为边, aici\frac{a_i}{c_i}为边权建立有向图, 循环配方表现为环

设循环配方中任意物品原有数量为xx,个 则每次循环后可变为为xikaici\frac {x}{\prod_{i}^k\frac{a_i}{c_i}}个,

要使物品不会变多, 则必须xxikaicix≥\frac {x}{\prod_{i}^k\frac{a_i}{c_i}}, 也即

ikaici1\prod_{i}^k\frac{a_i}{c_i}≥1

引入生产修正参数ww

ai×biw×ci×dia_i\times b_i\to w\times c_i \times d_i

可以理解为

aiwci×bidi\frac{a_i}{wc_i}\times b_i \to d_i

可推得

ikaiwci\prod_{i}^k\frac{a_i}{wc_i} 物品\to 物品

ww提出有

1wkikaici\frac1{w^k}\prod_{i}^k\frac{a_i}{c_i} 物品\to 物品

重复上文推论

nn种物品为点, mm种配方为边, aiwci\frac{a_i}{wc_i}为边权建立有向图, 循环配方表现为环

设循环配方中任意物品原有数量为xx,个 则每次循环后可变为为x1wkikaici\frac {x}{\frac1{w^k}\prod_{i}^k\frac{a_i}{c_i}}个,

要使物品不会变多, 则必须xx1wkikaicix≥\frac {x}{\frac1{w^k}\prod_{i}^k\frac{a_i}{c_i}}, 也即

ikaiciwk\prod_{i}^k\frac{a_i}{c_i}≥w^k

对于不同的环有不同的ikaici\prod_{i}^k\frac{a_i}{c_i}, 即要求在每一个环中都满足上式,即

min(ikaici)wk\min(\prod_{i}^k\frac{a_i}{c_i})≥w^k

且要求ww取最大, 则要求

min(ikaici)=wk\min(\prod_{i}^k\frac{a_i}{c_i})=w^k

化简得

w=min(ikaicik)w=\min\left(\sqrt[k]{\prod_{i}^k\frac{a_i}{c_i}}\right)

其中连乘可用对数处理

ln(ikaici)=ikln(aici)\ln(\prod_{i}^k\frac{a_i}{c_i})=\sum_i^k\ln(\frac{a_i}{c_i})

化简得

ikaici=eikln(aici)\prod_{i}^k\frac{a_i}{c_i}=e^{\sum_i^k\ln(\frac{a_i}{c_i})}

即要求

wk=emin(ikln(aici))w^k=e^{\min(\sum_i^k\ln(\frac{a_i}{c_i}))}

最终

w=min(eikln(aici)k)w=\min(\sqrt[k]{e^{\sum_i^k\ln(\frac{a_i}{c_i})}})
w=exp(min(ikln(aici)k))w=\exp\left(\min(\frac{\sum_i^k\ln(\frac{a_i}{c_i})}{k})\right)

则理解为求环的最小平均权重

ln(aici)\ln(\frac{a_i}{c_i})为边权建图G(V,E)G(V,E)

KarpKarp的最小平均权重环路算法

F[k][v]F[k][v]为从任意点出发,经过kk条边到达vv点的最短距离, 对每条边uvu\to v

F[k][v]=eE{F[k1][u]+w[u][v]}F[k][v]=\min_{e\in E}\{F[k-1][u]+w[u][v]\}
ans=vV{0kn1{F[n][v]F[k][v]nk}}ans = \min_{v\in V}\{\max_{0≤k≤n-1}\{\frac{F[n][v]-F[k][v]}{n-k}\}\}

详细请参考***********************************************************

代码

#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;
}