01分数规划

好久没发博客了……浅浅复活一下,讲个冷门些的算法。

算法目的:选出k组ai,bi使得  \frac{\sum ai }{\sum bi} 最大。

算法过程:

不妨考虑二分答案,那么答案的形式便是 \frac{\sum ai }{\sum bi}\geq k 的形式,则可通过移项转化为\sum ai - k*\sum bi\geq 0,进一步的,我们可以将求和合并,则有\sum ai-k*bi\geq 0,这便是二分检验的条件,只需挑出满足条件的k组即可,该算法考不出太新鲜的,至多是二分加优化。

那么我们来看一道例题

P4377 [USACO18OPEN] Talent Show G

简化一下题意:选出任意组ai,bi使得\frac{\sum ai }{\sum bi}最大,且\sum bi\geq W。那么看到该限制条件,只需在二分时使用01背包优化即可。最后输出为答案*1000向下取整。

代码:

#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const int maxn=1e5+10;
int n,w,a[maxn],b[maxn];
double f[maxn]; 
bool check(double mid){
	for(int i=1;i<=w;i++) f[i]=-1e9;
	for(int i=1;i<=n;i++){
		for(int j=w;~j;j--){
			f[min(w,j+b[i])]=max(f[min(w,j+b[i])],f[j]+a[i]-mid*b[i]);
		}
	}
	return f[w]>=0;
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>w;
	for(int i=1;i<=n;i++) cin>>b[i]>>a[i];
	double l=0,r=1e6;
	while(r-l>1e-4){
		double mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	cout<<(int)(1000*l)<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值