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; 
    } 
    
  • 相关阅读:
    Linux IO接口 监控 (iostat)
    linux 防火墙 命令
    _CommandPtr 添加参数 0xC0000005: Access violation writing location 0xcccccccc 错误
    Visual Studio自动关闭
    Linux vsftpd 安装 配置
    linux 挂载外部存储设备 (mount)
    myeclipse 9.0 激活 for win7 redhat mac 亲测
    英文操作系统 Myeclipse Console 乱码问题
    Linux 基本操作命令
    linux 查看系统相关 命令
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998319.html
Copyright © 2011-2022 走看看