美文网首页
最优比例生成树

最优比例生成树

作者: 碧影江白 | 来源:发表于2017-02-08 14:46 被阅读361次

    在网上看过的很多博客解析内容都是一样的,根本分不清哪一个是原版,更可笑的是甚至有的文字和代码是相矛盾的,而且叙述也不清晰,看起来真叫人头疼,所以我在这里说一下我的理解。
    最优比例生成树的概念为:
    在一个图中,每一条边都有benefit和cost两个参数值,要求根据图的信息生成一棵树,树的要求达到整棵树中的∑benefit[i]/∑cost[i]的值最大,即达到最大收益。
    在初遇到这种类型的时候,想到的第一种方法就是求解benefit[i]/cost[i]作为权值,求得最大生成树,但是结果是错误的,因为先求和再相除与先相除再求和是完全不一样的,看了百度上的相関解析才明白了最优生成树的求解。
    首先,所有的边都可以分两种状态,是否属于生成树,假设用数组x[i]=1/0来代表是与否,那么就可以写成:max(∑benefit[i]*x[i]/∑cost[i]*x[i])这就是0/1分数的规划问题
    在明白了这一步后,进行代换:

    由于i=1~n:
    benefit[0]*x[0]-max(r)*cost[0]*x[0]+
    benefit[1]*x[1]-max(r)*cost[1]*x[1]+
    benefit[2]*x[2]-max(r)*cost[2]*x[2]+
    .......+
    benefit[n]*x[n]-max(r)*cost[n]*x[n]=0
    可以看出来,以上式子经化简后得为:
    ∑( benefit[i]-max(r)*cost[i]) )* x[i]=0
    

    则可以看出该图的算法可以理解为一个已d[i]为边权(d[i]=benefit[i]-max(r)cost[i])的生成树,并且保证生成树的权值和为0,此时,max(r)为一个实数,以保证等式成立。
    由于max(r)未知,故无法求出图中x[i]的具体情况,简化以上叙述:



    当f(r)取值为max(r)时,z(max(r))=0。
    且根据d[i]的表达式可以得出,f(r)对于z(f(r))是单调递减函数。
    由于其具有的单调性,我们可以直接以此来进行二分法来对f(r)进行取值,检验该生成树的权值和是否为0即可。
    有没有感觉简单很多呢?以下是实现方法的叙述:



    f(r)是最优比例树所要求的最大值,因此我们要保证的是f(r)取最大值,与此同时z(f(r))的值要求为0,那么就可以将d[i]为权值做最小生成树,并检验最小生成树权值和取绝对值后与0进行无限靠近,在达到所要求精度后即可确认为检验成功。
    实现方法可以使用二分法,也可以使用迭代法,迭代法为
    实现代码如下所示:
    #include<stdio.h>
    #include<stdlib.h>
    #include<math.h>
    #define eqs 1e-6
    
    double prim(double benefit[5][5],double cost[5][5],double mid);
    
    int main(){
        double benefit[5][5] = {
            { 0,  30, 40, 20, 30 },
            { 30, 0,  10, 0,  0 },
            { 40, 10, 0,  50, 100 },
            { 20, 0,  50, 0,  40 },
            { 30, 0,  100,40, 0 } };
        double cost[5][5] = {
            { 0, 9, 7, 5, 2 },
            { 9, 0, 3, 6, 1 },
            { 7, 3, 0, 7, 8 },
            { 5, 6, 7, 0, 3 },
            { 2, 1, 8, 3, 0 } };
    
        int i, j, k, n, m;
        double max, min, id;
        double ans, low, high, mid;
        low = 0;
        int vis[8] = { 0 };
        /*直接使0结点为根节点*/
        min = 999999; max = 0;
        for (j = 0; j < 5; j++){
            for (k = 0; k < 5; k++){
                if (min > cost[j][k] && j != k)
                    min = cost[j][k];
                if (max < cost[j][k])
                    max = cost[j][k];
            }
        }
        
        high = (max - min) * 4 * 4; 
    
        while (true)
        {
            mid = (high + low) / 2;
            ans = prim(benefit,cost,mid);
            printf("%lf\n",fabs(ans));
            if (fabs(ans) <= eqs)
                break;
            if (ans < 0)
                high = mid;
            else
                low = mid;
        }
        system("pause");
    }
    
    double prim(double benefit[5][5], double cost[5][5], double mid){
        double ans=0;
        double solve[5][5];
        int i, j, k, n;
        double lowcost[8];
        int node[8] = {0};
        int vex[30] = {0};
        printf("%.3lf**\n",mid);
        for (i = 0; i < 5; i++){
            for (j = i; j < 5; j++){
                solve[i][j] = solve[j][i] = benefit[i][j] - mid*cost[i][j];
                printf("%.3lf ",solve[i][j]);
                if (i == j)
                    solve[i][i] = 0;
            }
            printf("\n");
        }
        k = 0;
        for (n = 1; n < 5; n++){
            
            for (i = 0; i > 5; i++){
                lowcost[i] = solve[k][i];
            }
            node[k] = 1;
    
            int id;
            double min = 999999;
            for (j = 0; j < 5; j++){
                if (min > lowcost[j] && node[j] != 1){
                    min = lowcost[j];
                    id = j;
                }
            }
            printf("%d--%d   \n",id,k);
            ans += solve[id][k];
            node[id] = 1;
            k = id;
        }
        return ans;
    }
    --------------------------------------------------------------------------------------
    迭代法在实现为prim传参时的方法:
     double a = 0, b;  
            while(1)  
            {  
                b = prim(benefit,cost, a);  
                if(fabs(a - b) < eps) break;  
                a = b;  
            }  
    在prim中的参数回传也需改为benefit/cost的和:
    ...
    ans_ben+=benefit[id][k];
    ans_cos+=cost[id][k];
    ...
    return ans_ben/ans_cost;
    

    相关文章

      网友评论

          本文标题:最优比例生成树

          本文链接:https://www.haomeiwen.com/subject/bfemittx.html