0/1 分数规划

0/1 分数规划

0/1 分数规划模型是指,给定整数 a1,a2,,anb1,b2,,bn,求一组解 xi[0,1],使下式最大/最小化:

i=1naixii=1nbixi

换句话说,选一定的 aibi 组成一个集合 S,求

aiSbiS

的最值。

这种问题的一般解决方法是设答案为 ans,即

ans=aibi ,

然后将式子变形,得

ans×bi+ai=0

(ans×bi+ai)=0

答案具有可二分性。所以我们解决这类问题的方法一般就是二分答案,初始 l=0,r=,每次判定 (mid×bi+ai) 的正负,直到找到在误差范围内最接近的答案。复杂度是 O(logV) 的。

还有一种更高级的方法叫做 Dinkelbach 迭代法,没用。

最优比率生成树

一般来说 0/1 分数规划不会直接考,而是套在一些别的东西中,比如最优比率生成树。题意一般是给定一个图,每条边有两个边权 ai,bi,求这张图的生成树的 aibi

依然二分,每次跑一遍 Kruskal 求出上面那个式子,然后二分答案判定即可。

[ARC026D] 道を直すお仕事

板子题,但注意到这题并不要求一定是棵生成树,所以边权为负的边一定是会选的,把所有边权为负的边选完之后再判断边权为正的边选不选。

using ll=long long;
constexpr int MAXN=10005,MAXM=10005;
constexpr double eps=1e-6;
int n,m;
struct Edge{
	ll u,v,c,t;
}e[MAXM];
int f[MAXN];
int fnd(int x){
	return f[x]==x?x:f[x]=fnd(f[x]);
}
double kruskal(double x){
	iota(f+1,f+n+1,1);
	int s=0;
	for(int i=1;i<=m;++i) if(x*e[i].t-e[i].c>=-eps) ++s;
	sort(e+1,e+m+1,[&](const Edge&a,const Edge&b){
		return x*a.t-a.c>x*b.t-b.c;
	});
	double res=0;
	for(int i=1;i<=s;++i){
		f[fnd(e[i].u)]=fnd(e[i].v);
		res+=x*e[i].t-e[i].c;
	}
	for(int i=s+1;i<=m;++i){
		int fx=fnd(e[i].u),fy=fnd(e[i].v);
		if(fx==fy) continue;
		f[fy]=fx;
		res+=x*e[i].t-e[i].c;
	}
	return res;
}

int main(){
	n=read(),m=read();
	for(int i=1;i<=m;++i) e[i]={read()+1,read()+1,read(),read()};
	double l=0,r=1e18;
	while(r-l>eps){
		double mid=(l+r)/2;
		if(kruskal(mid)<-eps) l=mid;
		else r=mid;
	}
	printf("%.4f\n",l);
	return 0;
}

P4951 [USACO01OPEN] Earthquake

这道题求的是 Faibi。同理,将原式变换为 F=(ans×bi+ai),每次二分答案判定和 F 的大小关系即可。

posted @   Laoshan_PLUS  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示