• Link with Game Glitch(负环)


    题意

    \(n\)个物品,\(m\)个转换,每\(ka_i\)\(b_i\)类物品可以换\(w \cdot kc_i\)\(d_i\)类物品。其中\(k\)为任意正实数。

    求最大的\(0 \leq w \leq 1\)使得不存在一种转换方式可以得到无限多的某类物品。

    题目保证当\(w = 1\)时,必然存在一种转换方式使得某类物品可以得到无限多个。

    (题目翻译来源于emofunc的讲解PPT)

    题目链接:https://ac.nowcoder.com/acm/contest/33187/D

    数据范围

    \(2 \leq n \leq 1000\)
    \(2 \leq m \leq 2000\)

    思路

    由于\(k\)为任意正实数,因此我们可以计算每个\(b_i\)可以换多少个\(d_i\),答案是\(\frac{w \cdot c_i}{a_i}\)

    然后我们再考虑什么情况下某类物品可以得到无限多个,不妨设该类物品为\(p_0\),且初始数量为\(1\)。使用\(p_0\)换取若干\(p_1\),然后\(p_1\)再换取若干\(p_2\),依此类推,最终\(p_{k-1}\)换取若干\(p_k\)\(p_k\)再换取若干\(p_0\)。如果一轮循环之后的\(p_0\)个数大于\(1\),则最终\(p_0\)可以无限获得。

    一轮循环之后的\(p_0\)个数为\(\frac{wc_1}{a_1} \cdot \frac{wc_2}{a_2} \cdots \frac{wc_k}{a_k} \cdot \frac{wc_0}{a_0}\)。为了方便计算,我们将个数取对数,可以将条件转化为:\(\sum\limits_{i=0}^k \log (\frac{wc_i}{a_i}) > 0\),即:\(\sum\limits_{i=0}^k -\log (\frac{wc_i}{a_i}) < 0\)

    因此,我们在\(b_i\)\(d_i\)之间连一条权值为\(-\log(\frac{c_i}{a_i})\)的有向边。然后二分\(w\)。最后使用spfa算法判断图中是否存在负环即可。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    
    using namespace std;
    
    const int N = 2010;
    const double eps = 1e-8;
    
    int n, m;
    int h[N], e[N], ne[N], idx;
    double w[N], d[N];
    bool st[N];
    int cnt[N];
    
    void add(int a, int b, double c)
    {
        e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
    }
    
    bool check(double x)
    {
        queue<int> que;
        for(int i = 1; i <= n; i ++) {
            que.push(i);
            st[i] = true;
            cnt[i] = 0;
            d[i] = 0;
        }
        double k = log(x);
        while(que.size()) {
            int t = que.front();
            que.pop();
            st[t] = false;
            for(int i = h[t]; ~i; i = ne[i]) {
                int j = e[i];
                if(d[j] > d[t] - w[i] - k) {
                    d[j] = d[t] - w[i] - k;
                    cnt[j] = cnt[t] + 1;
                    if(cnt[j] >= n) return false;
                    if(!st[j]) {
                        st[j] = true;
                        que.push(j);
                    }
                }
            }
        }
        return true;
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        memset(h, -1, sizeof h);
        for(int i = 0; i < m; i ++) {
            int a, b, c, d;
            scanf("%d%d%d%d", &a, &b, &c, &d);
            add(b, d, log(1.0 * c / a));
        }
        double l = 0.0, r = 1.0;
        for(int i = 0; i < 100; i ++) {
            double mid = (l + r) / 2;
            if(check(mid)) l = mid;
            else r = mid;
        }
        printf("%.10f\n", l);
        return 0;
    }
    
  • 相关阅读:
    【C#/WPF】限制GridSplitter分隔栏的滑动范围
    【C#】访问泛型中的List列表数据
    【C#学习笔记】反射的简单用法
    【C#】获取泛型<T>的真实类型
    【Unity】关于发射子弹、导弹追踪的逻辑
    【转】【Unity】四元数(Quaternion)和旋转
    【Unity】UGUI的Text各种小问题
    【火狐FireFox】同步失败后,书签被覆盖,如何恢复书签
    【转】【Unity】实现全局管理类的几种方式
    【Unity】动态调用其他脚本的函数
  • 原文地址:https://www.cnblogs.com/miraclepbc/p/16603542.html
Copyright © 2020-2023  润新知