分数规划小专题

一句话概括:浮点二分求解∑a/∑b的极值问题

引入:首先是这个题,浮点二分之后树上背包

进阶应用:最小(大)比率生成树/最小(大)比率生成环

以最大为例,关键就是考虑如果小了的话如何判断
生成树中,比如要让生成树的∑a/∑b最大,即∑a/∑b \(\leq r\),化简为∑(a-br) \(\leq 0\),如果当前二分的r小了的话,就会导致出现一个生成树的该值>0,利用这个判断。那如果该值 <=0 呢?r应该缩小。为什么呢?因为此时可能取不到r,这也是显然的。
最大生成环同理,化简出来是如果最大值取小了会有负环

如果要求最小生成环呢?化简出来是∑(a-br)\(\geq 0\),如果r取大了也是会有负环

POJ2728 最小比率生成树
完全图,要跑prim

// by Balloons
#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;

const int inf = 1 << 30;
const int maxn=5005;
const double eps=1e-6;

struct data{double x,y,h;}a[maxn];
double dis[maxn][maxn], h[maxn][maxn],p[maxn][maxn],line[maxn];
int vis[maxn];
int n;

double getdis(int x,int y){
	return sqrt((a[x].x-a[y].x)*(a[x].x-a[y].x) + (a[x].y-a[y].y)*(a[x].y-a[y].y));
}

double prim(double num){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			p[i][j] = h[i][j] - dis[i][j]*num;
	memset(vis,0,sizeof vis);
	memset(line,127,sizeof line);
	line[1] = 0;
	for(int i=1;i<=n;i++){
		int u = 0;
		for(int j=1;j<=n;j++)
			if(!vis[j] && line[j]<line[u]) u = j;
		vis[u] = 1;
		for(int j=1;j<=n;j++)
			if(!vis[j])line[j] = min(line[j],p[u][j]);
	}
	double sum=0;
	for(int i=1;i<=n;i++)sum += line[i];
	return sum;
}

int main(){
	while(scanf("%d",&n),n){
		for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].h);
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++){
				dis[i][j] = getdis(i,j);
				h[i][j] = fabs(a[i].h-a[j].h);
			}
		double l=0.0, r=100.0;
		while(r-l >= eps){
			double mid=(l+r)/2.0;
			if(prim(mid) >= 0)l=mid;
			else r=mid;
		}
		printf("%.3f\n",l);
	}

	return 0;
}

Luogu2868 & 1768 最大比率生成环
由上面的分析,写个spfa判负环即可。注意由于是单向图,起点的选择很重要,可以建个超级源点0连向所有点,从0开始跑。注意这样写的话负环的判断条件是大小为n+1而非n!
代码(1768)

// by SkyRainWind
#include <cstdio>
#include <queue>
#include <vector>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn=2e5+5;
const double eps = 1e-6;

int n,m;
int a[maxn],vis[maxn],cnt[maxn];
double dis[maxn];
struct node{int to;double t,f;};
vector<node>g[maxn];

int check(double r){
	for(int i=0;i<=n;i++)dis[i] = 1e9, vis[i] = cnt[i] = 0;
	
	queue<int>Q;
	Q.push(0);dis[0] = 0;vis[0] = 1;
	while(!Q.empty()){
		int x = Q.front();Q.pop();
		vis[x] = 0;	////////!!!
		for(node now : g[x]){
			int u = now.to;
			double v = r * now.t - now.f;
			if(dis[u] > dis[x] + v){
				dis[u] = dis[x] + v;
				cnt[u] = cnt[x] + 1;
				if(cnt[u] >= n+1)return 1;
				if(!vis[u]){
					vis[u] = 1;
					Q.push(u);
				}
			}
		}
	}
	return 0; 
}

signed main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y,v,p;scanf("%d%d%d%d",&x,&y,&v,&p);
		g[x].push_back((node){y,1.0 * p,1.0 * v});
	}
	for(int i=1;i<=n;i++)g[0].emplace_back((node){i,0,0});
	double l = 0.0, r = 200, ans = 1e9;
	while(fabs(r - l) > eps){
		double mid = (l+r)/2.0;
		if(check(mid))l = mid, ans = mid;
		else r = mid;
	}
	if(fabs(ans - 1e9) < eps)puts("-1");
	else printf("%.1f\n",ans);

	return 0;
}
posted @ 2022-09-17 19:35  SkyRainWind  阅读(11)  评论(0编辑  收藏  举报