pku 3621 01分数规划 sightseeing cows 解题报告

 

pku 3621 sightseeing cows 解题报告

题意:求存在一个环路,所有的点权之和/所以的边权之和 最大是多少?

 

算法:此题是对01分数规划的应用,那么首先明白01分数规划的思想.

 

01分数规划的思想的描述如下:令c=(c1,c2,…,cn)d=(d1,d2,…,dn)n维整数向量,那么一个0-1分数规划问题用公式描述如下:FP: 最小化(c1x1+…cnxn)/(d1x1…dnxn)=cx/dx xi{0,1}这里x表示列向量(x1,x2,…,xn)T .0-1值向量的子集Ω称作可行域,而x则是Ω的一个元素,我们称x为可行解。即可以简化为y=c/d.那么再演变一下:y-c/d=0.我们目标是求y.那么我们可以假设函数f(y)=y-c/d.

 

重要结论:

对于分数规划问题,有许多算法都能利用下面的线性目标函数解决问题。
Q(L): 
最小化 cx-Ldx xi{0,1}

z(L)Q(L)的最值。令x*为分数规划的最优解,并且令L*=(cx*)/(dx*)(注:分数规划的最值)。那么下面就容易知道了:
z(L) > 0
当且仅当 L<L*
z(L) = 0
当且仅当 L=L*
z(L) < 0
当且仅当 L>L*
此外,Q(L*)的最优解也能使分数规划最优化。因此,解决分数规划问题在本质上等同于寻找L=L*使z(L)=0

 

因此,求解f(y)=0,为其函数的最优解,即可以利用二分的思想逐步推演y,从而求得最优解.

 

回到题目,我们知道是求解segma(f[V])/segma(E[v])的最大值,同时每个结点对应一个点权,每条边对应一个边权,那么我们就可以联想到应用01分数规划的思想来求解.01分数规划是与二分紧紧联系在一起的.那么怎么应用二分求解呢?

 

我们首先想想当仅仅有2个结点环路的时候,问题就演变为f(y)=y-c/d,y是通过二分逐步推算出来的,那么我们的任务就变为在一定的精度范围内测试求解其最优解.y-c/d>0,y减少; y-c/d<0,y增大.2个结点之间,那么我们就可用重新将图的权变为y-c/d,这样问题就回到2个结点的环路是否存在负权回路,存在说明y-c/d<0,不存在y-c/d>0.从而进一步推算最优解y

 

AC代码:

#include <stdio.h>

#include <string.h>

#define M 1005

const double inf = 10000000000;

 

int l, p;

struct node

{

               int v, weight, next;

}graph[5005];

int pre[M], queue[M*M], count[M], f[M];

 

int spfa(double mid)

{

               int i, front, tail;

               double new_weight, dis[M];

               bool visit[M];

 

               memset(visit, 0, sizeof(visit));

               memset(count, 0, sizeof(count));

               for (i = 1; i <= l; i++)

               {

                               dis[i] = inf;

               }

               front = 0; tail = 1;

               queue[front] = 1; visit[1] = true; dis[1] = 0;

               while (tail > front)

    {

                               int t = queue[front++];

                               visit[t] = false;

                               for (i = pre[t]; i != -1; i = graph[i].next)

                               { 

                                              new_weight = graph[i].weight * mid - f[graph[i].v];

                                              if (dis[graph[i].v] > dis[t] + new_weight)

                                              {

                                                             dis[graph[i].v] = dis[t] + new_weight;

                                                             if (!visit[graph[i].v])

                                                             {

                                                                            visit[graph[i].v] = 1;

                                                                            queue[tail++] = graph[i].v;

                                                                            count[graph[i].v]++;

                                                                            //有负权环的时候,路径将不断的减小,那么count一定大于所有的结点之和

                                                                            if (count[graph[i].v] > l)

                                                                            {

                                                                                            return 1;

                                                                            }

                                                             }

                                              }

                               }

               }

               //没有负权环

               return 0;

}

 

void init()

{

               int i, a, b, c, top;

              

               for (i = 1; i <= l; i++)

               {

                               scanf("%d", &f[i]);

               }

               top = 0;

               memset(pre, -1, sizeof(pre));

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

               {

                               scanf("%d%d%d", &a, &b, &c);

                               graph[top].v = b;

                               graph[top].weight = c;

                               graph[top].next = pre[a];

                               pre[a] = top++;

                               //printf("%d %d %d %d/n", graph[top - 1].v, graph[top - 1].next, graph[top - 1].weight, head[top - 1]);

               }

}

 

int main()

{

               freopen("1.txt", "r", stdin);

 

               while (scanf("%d%d", &l, &p) != EOF)

               {

                               init();

                               //01规划,求最优值mid

                               double left = 0, right = 100;

                               //精度要求不高

                               for (int i = 0; i <= 20; i++)

                               {

                                              double mid = (left + right) / 2;

                                              //01规划:E * mid - f;其中最优解为E*mid-f=0的时候

                                              //当有负权环的时候,E * mid - f<0,mid需要增大,反之需要减小

                                              if (spfa(mid))  

                                              {

                                                             left = mid;

                                              }

                                              else

                                              {

                                                             right = mid;

                                              }

                               }

                               printf("%.2lf/n", left);

               }

 

               return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值