Title

Sightseeing Cows题解

题目

题目大意:给定一个有向图,每个点有一个权值\(f[x]\),每一条边有一个权值\(val[i]\),要求找出一个环,使得环上点权值之和除以换上所有边的权值的商最大。

这是一个经典的0/1分数规划问题,题目中的要求是\(\frac {\sum f[i]} {\sum val[i]}\)最大值
\(\frac {\sum f[i]} {\sum val[i]}=mid\)
\(\sum f[i]=mid \times \sum val[i]\)
我们可以把两种权值都放到边上,我们假设一条边的另外一个权值为这条边上的父亲节点的权值。
然后就有\(\sum f[i]-mid \times val[i] = 0\)
\(\sum mid \times val[i] - f[i]= 0\)
我们可以先二分一个\(mid\)值,然后重建图,结构与原图相等,边权变成\(\sum mid \times val[i] -f[i]\),然后用spfa算法判断与没有负环,有负环说明此时的\(mid\)小于最大值,则\(l=mid\)并更新答案,没有负环则\(r=mid\),注意实数二分的精度限制。

Code:

#include<bits/stdc++.h>
#define maxn 250000
using namespace std;
int vis[maxn],cnt[maxn];
int n,m,u,v,w;
int tot1=0,pre1[maxn],son1[maxn],now1[maxn];
long double val1[maxn],val2[maxn],ans=0,d[maxn];
int tot2=0,pre2[maxn],son2[maxn],now2[maxn];
int dian[maxn];
queue<int> q;
void put1(int x,int y,long double z)
{
    pre1[++tot1]=now1[x];
    now1[x]=tot1;
    son1[tot1]=y;
    val1[tot1]=z;
}
void put2(int x,int y,long double z)
{
    pre2[++tot2]=now2[x];
    now2[x]=tot2;
    son2[tot2]=y;
    val2[tot2]=z;
}
int spfa()
{
    
    memset(vis,0,sizeof(vis));
    while(!q.empty())q.pop();
    for(int i=1;i<=n;i++)
        d[i]=1532534223;
    int s=1;
    d[s]=0;vis[s]=1;cnt[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int x=q.front();q.pop();vis[x]=0;
        for(int i=now2[x];i;i=pre2[i])
        {
            int v=son2[i];
            if(d[v]>d[x]+val2[i])
            {
                d[v]=d[x]+val2[i];
                cnt[v]=cnt[x]+1;
                if(cnt[x]>n)return true;
                if(!vis[v])
                    q.push(v),vis[v]=1;
            }
        }
    }
    return false;
}
void build(long double L)
{
    memset(now2,0,sizeof(now2));
    tot2=0;
    for(int i=1;i<=n;i++)
   {
        for(int j=now1[i];j;j=pre1[j])
        {
            int v=son1[j];
            put2(i,v,L*val1[j]-dian[i]);
        }
    } 
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&dian[i]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        put1(u,v,w);
    }
    long double l=0,r=1e6;
    while(r-l>=0.00001)
    {
        long double mid=(l+r)/2;
        build(mid);
        if(spfa())
            ans=max(ans,mid),l=mid;
        else
            r=mid;
    }
    printf("%.2Lf\n",ans);
    
    return 0;
}
posted @ 2021-02-25 16:09  五百年前  阅读(41)  评论(0编辑  收藏  举报