01分数规划

给定 n n n 个二元组 ( v a l u e i , c o s t i ) (value_i,cost_i) (valueicosti),分别表示价值和代价
x i ∈ { 0 , 1 } x_i∈\{0, 1\} xi{0,1},表示第 i i i 个二元组是否选取
要求最大(小)化下式:
r = ∑ v a l u e i × x i ∑ c o s t i × x i r = \frac{ \sum{value_i} \times x_i } { \sum{cost_i} \times x_i } r=costi×xivaluei×xi


二分 m i d mid mid ,若最优值 ≥ m i d : ≥ mid: mid:
∑ v a l u e i × x i ∑ c o s t i × x i ≥ m i d \frac{ \sum{value_i} \times x_i } { \sum{cost_i} \times x_i } ≥ mid costi×xivaluei×ximid
∑ ( v a l u e i − m i d ∗ c o s t i ) × x i ≥ 0 \sum{(value_i - mid * cost_i)} \times x_i ≥ 0 (valueimidcosti)×xi0
也就是找到一组 { x i } \{x_i\} {xi} 使得 ∑ ( v a l u e i − m i d ∗ c o s t i ) \sum{(value_i - mid * cost_i)} (valueimidcosti) 最大
若找到的最大值 ≥ 0 ≥ 0 0,也就是 最优值大于 m i d mid mid,反之就是小于 m i d mid mid

详细证明请见01分数规划问题相关算法与题目讲解(二分法与Dinkelbach算法)


例题一:POJ 2976 Dropping tests

选取 n − k n-k nk 个二元组使得 ∑ a i ∑ b i \frac{ \sum{a_i} } { \sum{b_i} } biai 最大

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const double eps = 1e-5;
const int maxn = 1e3 + 5;
int n, k, a[maxn], b[maxn];
double c[maxn];

bool ck(double x){
	for(int i=1; i<=n; i++) c[i] = a[i] - x * b[i];
	sort(c+1, c+1+n);
	double res = 0;
	for(int i=k+1; i<=n; i++) res += c[i];
	return res >= eps;
}

signed main() {
	while(scanf("%d%d", &n, &k) && n){
		for(int i=1; i<=n; i++) scanf("%d", a+i);
		for(int i=1; i<=n; i++) scanf("%d", b+i);
		double l = 0, r = 1e5, mid;
		while((r - l) >= eps){
			mid = (l + r) / 2;
			if(ck(mid)) l = mid + eps;
			else r = mid - eps;
		}
		printf("%.0f\n", r * 100);
	}
}

例题二:P4322 [JSOI2016]最佳团体

选取 k k k 个二元组使得 ∑ p i ∑ s i \frac{ \sum{p_i} } { \sum{s_i} } sipi 最大
同时选取一个二元组有可能有一个必须先选的依赖二元组

因此二分后,用 n 2 n^2 n2 的树形背包,每个点的点权为 ∑ ( p i − m i d ∗ s i ) \sum{(p_i - mid * s_i)} (pimidsi)
我看题解里有在 d f s dfs dfs d p dp dp,然后说是 n 2 n^2 n2 的,但是代码却写成 n 3 n^3 n3 。。。
树形背包 n 2 n^2 n2 的写法,合并子树时,必须先 d p dp dp,然后再加上这个子树的 s i z e size size
或者在 d f s dfs dfs 序上 d p dp dp 也可,常数更小

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 2505;
const double eps = 1e-5;
int n, m, s[maxn], p[maxn], r[maxn];
int dfn[maxn], tot, nxt[maxn];
double dp[maxn][maxn], v[maxn];
vector <int> g[maxn];

void dfs(int u){
	dfn[u] = tot++;
	for(auto v : g[u]) dfs(v);
	nxt[dfn[u]] = tot;
}

inline double ck(double x){
	for(rint i=1; i<=n+1; i++)
		for(rint j=0; j<=m; j++)
			dp[i][j] = -1e9;
	for(rint i=1; i<=n; i++) v[dfn[i]] = p[i] - s[i] * x;
	for(rint i=0; i<=n; i++)
		for(rint j=0; j<=min(m, i); j++){
			dp[i+1][j+1] = max(dp[i+1][j+1], dp[i][j] + v[i]);
			dp[nxt[i]][j] = max(dp[nxt[i]][j], dp[i][j]);
		}
	return dp[n+1][m];
}

signed main() {
	scanf("%d%d", &m, &n); m++;
	for(rint i=1; i<=n; i++) {
		scanf("%d%d%d", s+i, p+i, r+i);
		g[r[i]].push_back(i);
	}
	dfs(0);
	double l = 0, r = 1e4, mid;
	while((r - l) >= eps){
		mid = (l + r) * 0.5;
		if(ck(mid) >= eps) l = mid + eps;
		else r = mid - eps;
	}
	printf("%.3f\n", r);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值