01分数规划

0x00 背景

求 :

\(max\tfrac{\sum_{i=1}^na_ix_i}{\sum_{i=1}^nb_ix_i}(x_i=0/1)\)

我们称此类问题为 “0/1分数规划”

Plan A

爆搜 , 时间复杂度 \(2^n\) , 寄

Plan B

二分法

0x01 二分法和0/1分数规划

part 1 怎么想到的 ?

我们先构造一个式子 : \(\sum_{i=1}^n(a_i-L*b_i)*x_i\ge0\)

我们不妨随便猜测一个值 \(L\) , 考虑是否存在一组解 \(\{x_1,x_2,...,x_n\}\) 满足上式

  1. 若存在一组解 , 那么变形得 \((\sum_{i=1}^na_ix_i)-L*(\sum_{i=1}^nb_ix_i)\ge0\) , 进一步地 :

\(存在\{x_1,x_2,...,x_n\}使得\tfrac{\sum_{i=1}^na_ix_i}{\sum_{i=1}^nb_ix_i}\ge L\)

也就是说 , \(L\)比我们要求的最大值小

  1. 若不存在 , 那么 :

\(对于所有\{x_1,x_2,...,x_n\} , \tfrac{\sum_{i=1}^na_ix_i}{\sum_{i=1}^nb_ix_i}< L\)

也就是说 , \(L\)比我们要求的最大值小

显然 , 我们求答案的过程是满足单调性的 , 满足使用二分进行求解的要求 , 因此是可以考虑使用二分求解的

part 2 怎么做的?

我们二分 \(L\) 的时候如何进行 \(check\) 呢 ?

上面最容易验证的就是\(\sum_{i=1}^n(a_i-L*b_i)*x_i\ge0\) 了 , 验证的方法非常简单 , 只需要从那些\((a_i-L*b_i)\) 能中选出正数即可

二分结束 , \(L\)即为我们需要的解

0x02 代码实例

[Poj 2976]Dropping tests

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
struct node{
	int a,b;
	double y;
}p[N];
bool comp(node a,node b){
	return a.y>b.y;
}
int n,k;
bool check(double x){
	for(int i=1;i<=n;i++) 
		p[i].y=p[i].a*1.0-x*p[i].b;
	sort(p+1,p+n+1,comp);//排序,选择最大的(n-k)个
	double f=0;
	for(int i=1;i<=k;i++)
		f+=p[i].y;//计算结果
	return f<0;
}
int main(){
	while(scanf("%d%d",&n,&k)==2 && n+k){
		k=n-k;
		for(int i=1;i<=n;i++) scanf("%d",&p[i].a);
		for(int i=1;i<=n;i++) scanf("%d",&p[i].b);
		double L=0,R=0;
		for(int i=1;i<=n;i++)
			R+=p[i].a;
		for(int i=1;i<=50;i++){//本题二分50次即可
			double mid=L+(R-L)/2;
			if(check(mid)) R=mid;
			else L=mid;
		}
		printf("%d\n",(int)(100*(L+0.005)));
	}
	return 0;
}

0x03 应用场景

0/1分数规划本身看着是比较简单的 , 但它常常和一些特殊场景结合起来考 , \(ex. :\)

  1. 最优比率背包(+0/1背包)(T1.最佳团队,T2.Talent show G)
  2. 最优比率生成树(+生成树)
  3. 最优比率环(+最短路径负环判断)
  4. 最大密度子图(+网络流)

( 题目后续会更新 )

posted @ 2022-11-10 18:20  羊扬羊  阅读(35)  评论(0编辑  收藏  举报