POJ 3621 Sightseeing Cows

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

  这两天一直在复习代码,因为好久都不写东西了,而且转了语言,除了Dinic我什么都不会写……

  题目大意:给你一个有向图,边有权(T),点有权(F),使求一个环,使得最大化∑(F)/∑(T)。

  这是一个最优比率环问题,与上一个最有比率生成树问题相似,都是01分数规划的考点。

  设 ans≥∑(F)/∑(T) , 有

    ∑(F)-∑(T)*ans≤0

    ∑(F-T*ans)≤0

  我们可以二分ans,将图的边权变为 F-T*ans,用SPFA判断图中是否有负权回路。如果图中有负权回路,则当前回路中 ∑(F)-∑(T)*ans<0,继而推得∑(F)/∑(T)≤ans,为符合条件的解,ans需要向上二分;反之,ans需要向下二分。

  

难看的代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <cmath>
#define DINF 10000000.0
#define mm 10000
#define mn 1001
#define eps 1e-3
using namespace std;

queue<int> q;
int x,y,n,m,cnt[mn];
double dist[mn],F[mn],z;
bool vis[mn];
struct EDGE{
	int pnt;
	double dist;
	EDGE *pre;
	EDGE (){}
	EDGE(int _pnt,double _dist,EDGE *_pre):pnt(_pnt),dist(_dist),pre(_pre){}
}Edge[mm],*SP=Edge,*edge[mm];

inline void addedge(int a,int b,double c){
	edge[a]=new(++SP)EDGE(b,c,edge[a]);
}

bool SPFA(double ans){
	memset(cnt,0,sizeof(cnt));
	for(int i=2;i<=n;i++) dist[i]=DINF;
	memset(vis,false,sizeof(vis));
	while(!q.empty()) q.pop();
	q.push(1);dist[1]=0;
	while(!q.empty()){
		int i=q.front();q.pop();vis[i]=false;
		for(EDGE *j=edge[i];j;j=j->pre)
			if(dist[j->pnt]>dist[i]+ans*j->dist-F[j->pnt]){
				dist[j->pnt]=dist[i]+ans*j->dist-F[j->pnt];
				if(!vis[j->pnt]){
					vis[j->pnt]=true;
					if(++cnt[j->pnt]==n) return true;
					q.push(j->pnt);
				}
			}
	}
	return false;
}
	
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lf",&F[i]);
	for(int i=1;i<=m;i++){
		scanf("%d%d%lf",&x,&y,&z);
		addedge(x,y,z);
	}
	double low=0,high=DINF;
	while(high-low>eps){
		double mid=(low+high)/2.0;
		if(SPFA(mid)) low=mid;
		else high=mid;
	}
	printf("%.2lf\n",low);
	return 0;
}

  

原文地址:https://www.cnblogs.com/Delostik/p/2119329.html