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;

}

  

你可能感兴趣的:(poj)