POJ-3621-Sightseeing Cows(最优比率环)

题目链接:http://poj.org/problem?id=3621

题目大意:给出一个有向图,每个节点有一个权值val,每条边有一个长度len。奶牛要逛不少于两个点,并且最后要回到原点。求Ans= 所逛过的节点权值之和 / 经过的边的距离之和 的最大Ans。

思路:讨论区有个大佬证明了最终答案必定是简单环。

证明如下:

假设最优解不是简单环,则其中必定有一个重复点,设为c1,对于这个点隔开到也是两个环,我们设两个环中除了这个点其他点权值和分别为,c2,c3;边权值为 a1,s2;
由于它是最优解所以有 :
 1.(c1+c2+c3)/(a1+a2) > (c1+c2)/a1
 2.(c1+c2+c3)/(a1+a2) > (c2+c3)/a2
由1有:a1*c3 > a2*(c1+c2)   
由2有:a2*c1 > a1*(c2+c3)
所以:a1*c3 > a2*c2+a1*(c2+c3) 
显然错误,至此可以有结论,最优解必定是简单环

清晰易懂,由于牛所走的路必定为简单环,因此我们找到一个环Ans满足

Ans\geq \frac{\sum Val_i*X_i}{\sum Len_i*X_i}

=> \sum [(Val_i -Ans*Len_i)*X_i]\leq 0

=>F(Ans)=\sum [(Val_i -Ans*Len_i)*X_i]\leq 0

所以,对于所有的环,都应该为负环才符合要求(判断有没有正环),但是这样并不容易写。因此我们提出一个负号:

=>F(Ans)=\sum [(Ans*Len_i-Val_i)*X_i]\geq 0。F(Ans)>0的含义就是当前Ans 的值较大。(带一个值会比较清晰了解比如F(Ans)=1)。

这样,如果存在一个负环的话,就说明此时的Ans不是最优的,应该增加。

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
 
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
 
#define ll long long
#define Pair pair<double,double>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
 
const int MAXN=5e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)

struct Node{
	int v,nxt;
	double len;
	Node(int _v=0,double _len=0,int _nxt=0){
		v=_v;len=_len;nxt=_nxt;
	}
};
Node Edge[MAXN];
int Head[MAXN],Ecnt;
int Stk[MAXN],tot;
double Val[MAXN],Dis[MAXN];
int Vis[MAXN],Cnt[MAXN];
int n,m;

void Intt(){
	clean(Head,-1);Ecnt=0;
}
void AddEdge(int a,int b,double val){
	Edge[Ecnt]=Node(b,val,Head[a]);
	Head[a]=Ecnt++;
}
int Judge(double mid){
	for(int i=1;i<=n;++i){
		Dis[i]=0;Vis[i]=1;Cnt[i]=1;
		Stk[tot++]=i;
	}
	while(tot){
		int u=Stk[--tot];Vis[u]=0;
		for(int i=Head[u];i+1;i=Edge[i].nxt){
			int v=Edge[i].v;
			double temp=Edge[i].len*mid-Val[v];
			if(Dis[v]>Dis[u]+temp){
				Dis[v]=Dis[u]+temp;
				if(Vis[v]==0){
					Vis[v]=1;Cnt[v]++;
					Stk[tot++]=v;
					if(Cnt[v]>n) return 1;
				}
			}
		}
	}return 0;
}
int main(){
	Intt();
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%lf",&Val[i]);
	}
	for(int i=1;i<=m;++i){
		double a,b,c;scanf("%lf%lf%lf",&a,&b,&c);
		AddEdge(a,b,c);
	}
	double l=0.0,r=1000.0,mid;
	while(l<r-EPS){
		mid=(l+r)/2.0;
		if(Judge(mid)) l=mid;
		else r=mid;
	}printf("%.2f\n",r);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值