poj 3621 Sightseeing Cows (最优比率环)

本文探讨了一个整数规划问题,旨在找到一个环形路径,使得欢乐值与行走距离的比例最大。通过数学证明简化问题,利用01分数规划和SPFA算法判断是否存在正权环。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

题意:

给定 一些 点的 欢乐值 ,和一些点的距离 ,求 是否存在一个环使得 ,欢乐值 /走的总距离,   最大 ;

题解:整数规划 问题。 

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

 

解决了蛋疼的问题之后,就是01分数规划的部分了,我们只需要计算出D数组后找找有没有正权环即可,用spfa(单源最短路径) 判断即可。
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cmath>
  4 #include<iostream>
  5 #include<algorithm>
  6 #include< set>
  7 #include<map>
  8 #include<queue>
  9 #include<vector>
 10 #include< string>
 11  #define Min(a,b) a<b?a:b
 12  #define Max(a,b) a>b?a:b
 13  #define CL(a,num) memset(a,num,sizeof(a));
 14  #define eps  1e-6
 15  #define inf 10001000
 16 
 17  #define ll   __int64
 18 
 19  #define  read()  freopen("data.txt","r",stdin) ;
 20  const  double pi  = acos(- 1.0);
 21  const  int maxn =  1024;
 22 
 23  using  namespace std;
 24  struct node
 25 {
 26      int u;
 27      int v;
 28      int len ;
 29      int next ;
 30 }p[maxn* 5] ;
 31  int n , m ;
 32  int cnt , head[maxn],num[maxn],a[maxn];
 33  double dis[maxn];
 34  int vis[maxn] ;
 35  void add( int u, int v, int len )
 36 {
 37     p[cnt].v = v;
 38     p[cnt].len = len;
 39     p[cnt].next = head[u] ;
 40     head[u] = cnt++ ;
 41 }
 42 
 43 queue< int>que;
 44  bool spfa( double mid)
 45 {
 46      int  i ;
 47      while(!que.empty())que.pop() ;
 48     
 49      for(i =  1 ; i <= n;i++)
 50     {
 51         dis[i] = -inf;
 52 
 53 
 54     }
 55 
 56     que.push( 1) ;
 57     
 58     dis[ 1] =  0;
 59     
 60     CL(vis, 0) ;
 61     
 62     vis[ 1] =  1;
 63 
 64     CL(num, 0) ;
 65 
 66 
 67 
 68      while(!que.empty())
 69     {
 70          int u = que.front() ;que.pop() ;
 71         vis[u] =  0 ;
 72          for(i = head[u];i !=- 1 ;i = p[i].next)
 73         {
 74              int v = p[i].v ;
 75              int len = p[i].len ;
 76              double t = a[v] - mid * len ;
 77              if(dis[u]  + t >  dis[v])
 78             {
 79                 dis[v] = t + dis[u];
 80                num[v] ++;
 81                  if(!vis[v])
 82                 {
 83                     que.push(v) ;
 84                     vis[v] =  1 ;
 85 
 86 
 87                 }
 88                  if(num[v] >= n)  return  true ;
 89 
 90 
 91 
 92             }
 93         }
 94 
 95     }
 96      return  false ;
 97 }
 98  int main()
 99 {
100      int i ;
101      int x,y;
102      int d;
103      // read() ;
104       while(scanf( " %d%d ",&n,&m)!=EOF)
105     {
106         cnt =   0 ;
107         CL(head,- 1) ;
108          for(i =  1  ;i <=  n;i++)
109           scanf( " %d ",&a[i]) ;
110          for(i =  0  ; i< m;i++)
111         {
112             scanf( " %d%d%d ",&x,&y,&d) ;
113             add(x,y,d);
114 
115         }
116          double  l =  0, r =  2500,mid;
117          double ans = - 1;
118          while(r - l >= eps)
119         {
120             mid = (l + r)/ 2.0 ;
121              if(spfa(mid))
122             {
123                 ans = mid;
124                 l = mid ;
125             }
126              else r = mid ;
127         }
128 
129          if(ans <  0)printf( " 0\n "); // 判断是否有环
130           else
131         printf( " %.2lf\n ",ans) ;
132 
133 
134     }
135 }

 


 


 

转载于:https://www.cnblogs.com/acSzz/archive/2012/10/06/2713011.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值