zoukankan      html  css  js  c++  java
  • 【洛谷P1768】天路【负环】【二分】【数论】

    题目大意:

    题目链接:https://www.luogu.org/problemnew/show/P1768
    一个有向图,每条边有权值v[i],p[i]v[i],p[i],你需要找到一个环,使得v[i]p[i]frac{sum v[i]}{sum p[i]}尽量大。


    思路:

    看到v[i]p[i]frac{sum v[i]}{sum p[i]},很容易想到是一道01分数规划问题。
    我们设ans=max{v[i]p[i]}ans=max{frac{sum v[i]}{sum p[i]}},那么对于任意的v[i]p[i]frac{sum v[i]}{sum p[i]},都有
    v[i]p[i]ansfrac{sum v[i]}{sum p[i]}leq ans
    移项得
    v[i]ans×p[i]sum v[i]leq ans imes sum p[i]
    再次移项得
    (p[i]×ans)v[i]0sum(p[i] imes ans)-sum v[i]geq 0
    也就是说
    (p[i]×ansv[i])0sum(p[i] imes ans-v[i])geq 0
    于是我们可以把所有的边的边权更改为p[i]×ansv[i]p[i] imes ans-v[i],只要现在图中有任意一个环得边权和为负数,那么ansans就是不符合要求的。
    那就二分ansans。题目中说了保证答案不超过200,于是时间复杂度就是O(nm log(200))O(nm log(200))


    温馨提示:

    • 这道题卡bfsbfsspfaspfa。请使用dfsdfs版本的spfaspfa
    • 图是不一定连通的。所以可以建立一个超级源点0,连向所有的边。spfaspfa直接从0开始跑。

    代码:

    #include <cstdio>
    #include <queue>
    #include <cstring>
    using namespace std;
    
    const int N=7010;
    const int M=30010;
    int n,m,x,y,v,p,tot,head[N];
    double l,r,mid,dis[N];
    bool vis[N];
    
    struct edge
    {
        int to,next;
        double v,p,dis;
    }e[M];
    
    void add(int from,int to,int v,int p)
    {
        e[++tot].to=to;
        e[tot].v=(double)v;
        e[tot].p=(double)p;
        e[tot].next=head[from];
        head[from]=tot;
    }
    
    bool spfa(int x)  //dfs版spfa1求负环
    {
        vis[x]=1;
        for (int i=head[x];~i;i=e[i].next)
        {
            int y=e[i].to;
            if (dis[y]>dis[x]+e[i].dis)
            {
                if (vis[y]) return 0;
                //dfs版不用cnt数组,如果访问到一个点时,这个点还在栈里,说明有负环
                dis[y]=dis[x]+e[i].dis;
                vis[y]=1;
                if (!spfa(y)) return 0;
            }
        }
        vis[x]=0;
        return 1;
    }
    
    int main()
    {
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&x,&y,&v,&p);
            add(x,y,v,p);
        }
        for (int i=1;i<=n;i++)
            add(0,i,0,0);
        l=0;
        r=200;
        while (r-l>0.01)
        {
            mid=(l+r)/2;
            for (int i=1;i<=m;i++)
                e[i].dis=e[i].p*mid-e[i].v;
            for (int i=0;i<=n;i++)
                dis[i]=1e9,vis[i]=0;
            dis[0]=0;
            if (spfa(0)) r=mid;
                else l=mid;
        }
        if (l==0) printf("-1");
            else printf("%0.1lf",l);
        return 0; 
    } 
    
  • 相关阅读:
    go正则解析log文件
    go的堆排序
    golang实现websocket
    golang接口
    erlang时间操作
    golang函数
    golang for循环
    golang 条件语句if,switch
    golang内建变量类型
    区间存在幂个数
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998318.html
Copyright © 2011-2022 走看看