01分数规划 ——最有比率环 pku 3621 Sightseeing Cows

http://poj.org/problem?id=3621

题意:

给定一张图,边上有花费,点上有收益,点可以多次经过,但是收益不叠加,边也可以多次经过,但是费用叠加。求一个环使得收益和/花费和最大,输出这个比值。

思路:(转载)

 首先的一个结论就是,不会存在环套环的问题,即最优的方案一定是一个单独的环,而不是大环套着小环的形式。这个的证明其实非常的简单,大家可以自己想一下(提示,将大环上的收益和记为x1,花费为y1,小环上的为x2,y2。重叠部分的花费为S。表示出来分类讨论即可)。有了这个结论,我们就可以将花费和收益都转移到边上来了,因为答案最终一定是一个环,所以我们将每一条边的收益规定为其终点的收益,这样一个环上所有的花费和收益都能够被正确的统计。

解决了蛋疼的问题之后,就是01分数规划的部分了,我们只需要计算出D数组后找找有没有正权环即可,不过这样不太好,不是我们熟悉的问题,将D数组全部取反之后,问题转换为查找有没有负权环,用spfa或是bellman_ford都可以。这道题目就是典型的不适合用Dinkelbach,记录一个负权环还是比较麻烦的,所以二分搞定。

上面讲的挺清晰的。。

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <List>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))

#define Min(a,b) (a) > (b)? (b):(a)

#define Max(a,b) (a) > (b)? (a):(b)



#define ll __int64

#define inf 0x7f7f7f7f

#define MOD 1073741824

#define lc l,m,rt<<1

#define rc m + 1,r,rt<<1|1

#define pi acos(-1.0)

#define test puts("<------------------->")

#define maxn 100007

#define M 5007

#define N 1007

using namespace std;

//freopen("din.txt","r",stdin);



const double eps = 1e-6;





struct node

{

    int u,v;

    double w;

    int next;

}g[M];

int head[N],ct;



double val[N];

double dis[N];

bool vt[N];

int que[1000003],l,r;

int num[N];



int n,m;



int dblcmp(double x)

{

    if (x > eps) return 1;

    else if (x < -eps) return -1;

    else return 0;

}

void add(int u,int v,int w)

{

    g[ct].v = v;

    g[ct].w = w;

    g[ct].next =  head[u];

    head[u] = ct++;

}

int spfa(double mid)

{

    int i;

    l = r = 0;

    for (i = 0; i < n; ++i)

    {

        dis[i] = inf;

        num[i] = 0;

        vt[i] = false;

    }

    dis[0] = 0;

    que[r] = 0;

    num[0] = 1;

    vt[0] = true;



    while (l <= r)

    {

        int u = que[l++];

        vt[u] = false;

        for (int j = head[u]; j != -1; j = g[j].next)

        {

            int i = g[j].v;

            if (dis[i] > dis[u] + mid*g[j].w - val[i])

            {

                dis[i] = dis[u] + mid*g[j].w - val[i];

                if (!vt[i])

                {

                    vt[i] = true;

                    que[++r] = i;

                    if (++num[i] > n) return 1;//进入队列的次数超过n次,表示存在负环

                }

            }

        }

    }

    return 0;

}

int main()

{

    int i;

    double z;

    int x,y;

    while (~scanf("%d%d",&n,&m))

    {

        CL(head,-1); ct = 0;

        double l = 0;

        double r = 0;

        for (i = 0; i < n; ++i)

        {

            scanf("%lf",&val[i]);

            r += val[i];

        }



        double pathMin = inf;

        for (i = 0; i < m; ++i)

        {

            scanf("%d%d%lf",&x,&y,&z);

            add(x - 1,y - 1,z);//稀疏图有临界表存

            pathMin = min(pathMin,z);

        }

        r /= pathMin;

        double mid = 0,ans = 0;

        while (dblcmp(l - r) < 0)

        {

            mid = (l + r)/2;

            if (spfa(mid))

            {

                ans = mid;

                l = mid;

            }

            else r = mid;

        }

        printf("%.2lf\n",ans);

    }

    return 0;

}

  

 

你可能感兴趣的:(pku)