📚【模板】分数规划

\(\text{Loj #149. 01 分数规划}\)


给定\(n\)个物品和每个物品的属性\(a_i,b_i\)

请求出:

\[\max\left(\frac{\sum\limits_{i=1}^{n}a_i\times x_i}{\sum\limits_{i=1}^{n}b_i\times x_i}\right) \]

其中\(x_i\in \left\{0,1\right\}\),并且恰有\(k\)\(x_i=1\)


实际上需要二分。

判断一个结果\(mid\)是否可行:

\[\begin{aligned} &\frac{\sum\limits_{i=1}^{n}a_i\times x_i}{\sum\limits_{i=1}^{n}b_i\times x_i}>mid\\ \implies&\sum\limits_{i=1}^{n}x_i\times \left(a_i-mid\times b_i\right) > 0 \end{aligned}\]


#include <stdio.h>
#include <bits/stl_algobase.h>
#include <bits/stl_algo.h>
const int N = 131026;
const double eps = 1e-8;
int n, k;
int a[N], b[N];
double c[N];
bool check(double now) {
	double res = 0;
	for(int i = 1;i <= n;++i) 
		c[i] = a[i]-now*b[i];
	std :: sort(c+1,c+n+1);
	for(int i = 1;i <= k;++i) 
		res += c[n-i+1];
	return (res > 0);
}
signed main() {
	scanf("%d %d",&n,&k);
	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 = 998244353;
	while(r-l > eps) {
		double mid = (l+r)/2.0;
		if(check(mid)) l = mid;
		else r = mid;
	}
	printf("%.4lf",l);
}

\(\text{luogu P4322 [JSOI2016]最佳团体}\)

#include <stdio.h>
#include <bits/stl_algobase.h>
#include <string.h>
const int N = 2048+512;
const double eps = 1e-8;
struct EDGE {
	int t, next;
} edge[N<<1];
int edge_tot, head[N];
void add_edge(int f,int t) {
	edge[++edge_tot].next = head[f];
	edge[edge_tot].t = t;
	head[f] = edge_tot;
}
int n, k;
int s[N], p[N];
int size[N];
double dp[N][N];
double val[N];
void predfs(int u) {
	size[u] = 1;
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].t;
		predfs(v);
		size[u] += size[v];
	}
}
void dfs(int u) {
	dp[u][1] = val[u];
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].t;
		dfs(v);
		for(int j = std :: min(size[u],k+1);j;--j) 
			for(int l = 0;l <= std :: min(size[v],j-1);++l) 
				dp[u][j] = std :: max(dp[u][j],dp[u][j-l]+dp[v][l]);
	}
}
bool check(double now) {
	for(int i = 1;i <= n;++i) 
		val[i] = p[i]-now*s[i];
	for(int i = 0;i <= n;++i) 
		for(int j = 1;j <= k+1;++j) 
			dp[i][j] = -98244353;
	dfs(0);
	return dp[0][k+1] >= 0;
}
signed main() {
	scanf("%d %d",&k,&n);
	for(int i = 1, f;i <= n;++i) {
		scanf("%d %d %d",&s[i],&p[i],&f);
		add_edge(f,i);
	}
	predfs(0);
	double l = 0, r = 65536;
	while(r-l > eps) {
		double mid = (l+r)/2.0;
		if(check(mid)) l = mid;
		else r = mid;
	}
	printf("%.3lf",l);
}

\(\textrm{luogu P1642 规划}\)

一个分数规划套树背包

套路跟题号一样经典

对于价值 \(a_i\) 和污染 \(b_i\),我们要求一个以 \(u\) 为根的大小为 \(n-m\) 的联通块的 \(\sum a_i-mid\times b_i\)

在树上 DP 即可。

#include <iostream>
#include <cstring>
const int N = 101;
const double eps = 1e-6;
const double inf = 1e9;
struct EDGE {
	int t, next;
} edge[N<<1];
int edge_tot, head[N];
void add_edge(int f,int t) {
	edge[++edge_tot].next = head[f];
	edge[edge_tot].t = t;
	head[f] = edge_tot;
}
int n, m;
int a[N], b[N];
double dp[N][N], val[N];
int size[N];
void dfs(int u,int p) {
	size[u] = 1;
	for(int i = head[u], v;i;i = edge[i].next) {
		v = edge[i].t;
		if(v == p) 
			continue;
		dfs(v,u);
		size[u] += size[v];
		for(int j = std :: min(m,size[u]);j >= 0;--j) 
			for(int k = 0;k <= std :: min(j,size[v]);++k) 
				dp[u][j] = std :: max(dp[u][j],dp[u][j-k]+dp[v][k]);
	}
	for(int i = std :: min(m,size[u]);i;--i) 
		dp[u][i] = dp[u][i-1]+val[u];
}
bool check(double x) {
	for(int i = 1;i <= n;++i) 
		for(int j = 1;j <= m;++j) 
			dp[i][j] = -inf;
	for(int i = 1;i <= n;++i) 
		val[i] = 1.0*a[i]-x*b[i];
	dfs(1,0);
	for(int i = 1;i <= n;++i) 
		if(dp[i][m] > -eps) 
			return true;
	return false;
}
int main() {
	scanf("%d %d",&n,&m);
	m = n-m;
	for(int i = 1;i <= n;++i) 
		scanf("%d",&a[i]);
	for(int i = 1;i <= n;++i) 
		scanf("%d",&b[i]);
	for(int i = 1, u, v;i < n;++i) {
		scanf("%d %d",&u,&v);
		add_edge(u,v);
		add_edge(v,u);
	}
	double l = 0, r = 1e6;
	while(r-l > eps) {
		double mid = (l+r)/2.0;
		if(check(mid)) 
			l = mid;
		else 
			r = mid;
	}
	printf("%.1lf\n",l);
	return 0;
}
posted @ 2022-07-27 11:14  bikuhiku  阅读(29)  评论(0编辑  收藏  举报