BZOJ-1690: [Usaco2007 Dec]奶牛的旅行

[文章目录]

Description

给出一张n个点,m条边有向图,每个点有点权,边有边权,求一个起点终点相同的路径使得路径上的
点权/ 边权*该边走的次数最大。输出这个比值。
n<=1000,m<=5000

一见到比值最大化直接想01分数规划。
设想这个路径的样子:显然路径上至少存在一个环。假设存在多个环,我们发现其中任意一个都可以单选。
假想有两个环,一个比值大,另一个比值小。他们合起来的比值肯定小于其中大的哪一个环。加上走重点或边代价变大,收益不变的情况,显然答案对应的路径肯定能只用一个环表示。

那么问题就变成了求一个最优比率环。回归到分数规划,将函数归为边权上,那么只要有一个正权环就说明解能够更优。

spfa判正权环。求最长路径,如果有一个点入队了超过n次,说明就存在正权环。跑一次的极限复杂度为O(n*m),实际上为O(跑得过)。
图论有坑啊,待填啊。

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,f[1100],num[1100];
int head[1100],nxt[5100],to[5100],cnt,data[5100];
void add(int x,int y,int z)
{
    to[++cnt]=y;
    nxt[cnt]=head[x];
    head[x]=cnt;
    data[cnt]=z;
}
double dis[1100];
queue<int>q;
bool v[1100];
bool check(double k)
{
    for(int i=1;i<=n;i++) dis[i]=-1000000000;
    memset(v,0,sizeof(v)); memset(num,0,sizeof(num));
    while(!q.empty()) q.pop();
    q.push(1); v[1]=1; dis[1]=0;
    int x,y;
    while(!q.empty())
    {
        x=q.front(); q.pop(); v[x]=0;
        for(int i=head[x];i;i=nxt[i])
            if(dis[y=to[i]]<dis[x]+f[to[i]]-k*data[i])
            {
                dis[y]=dis[x]+f[y]-k*data[i];
                if(!v[y]) q.push(y),num[y]++,v[y]=1;
                if(num[y]>n) return true;
            }
    }
    return false;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&f[i]);
    int x,y,z;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    double l=0,r=10000.0,mid;
    while(r-l>1e-4)
    {
        mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    printf("%.2lf\n",l);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注