【洛谷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]ans\frac{\sum v[i]}{\sum p[i]}\leq ans
移项得
v[i]ans×p[i]\sum v[i]\leq ans\times \sum p[i]
再次移项得
(p[i]×ans)v[i]0\sum(p[i]\times ans)-\sum v[i]\geq 0
也就是说
(p[i]×ansv[i])0\sum(p[i]\times ans-v[i])\geq 0
于是我们可以把所有的边的边权更改为p[i]×ansv[i]p[i]\times 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; 
} 
posted @ 2019-02-15 10:05  全OI最菜  阅读(108)  评论(0编辑  收藏  举报