前置知识:01分数规划(OI wiki摘录)
是什么:求一组极值

解法:二分答案,求一组数据的和是否满足不等式

注意细节:浮点数处理误差:eps = 3e-4
代码:
点击查看代码
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
int read() {
int X = 0, w = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9') X = X * 10 + c - '0', c = getchar();
return X * w;
}
constexpr int N = 100000 + 10;
constexpr double eps = 1e-6;
int n;
double a[N], b[N];
bool check(double mid) {
double s = 0;
for (int i = 1; i <= n; ++i)
if (a[i] - mid * b[i] > 0) // 如果权值大于 0
s += a[i] - mid * b[i]; // 选这个物品
return s > 0;
}
int main() {
// 输入
n = read();
for (int i = 1; i <= n; ++i) a[i] = read();
for (int i = 1; i <= n; ++i) b[i] = read();
// 二分
double L = 0, R = 1e9;
while (R - L > eps) {
double mid = (L + R) / 2;
if (check(mid)) // mid 可行,答案比 mid 大
L = mid;
else // mid 不可行,答案比 mid 小
R = mid;
}
// 输出
printf("%.6lf\n", L);
return 0;
}
题意:
树上每个点都有自己的价值和重量,问最大性价比是多少?(性价比 = 价值/体积)
思路:
分数规划 + 树上背包
其实就是转化为在树上选点,进行01分数规划
代码:
点击查看代码
/*
分数规划 + 简单树上背包
分数规划:二分求解答案,将所有a[i] - mid*b[i] >= 0的式子累加起来就是答案
分数规划:
权值更新为 g[i] = v[i] - mid * w[i]
f[0][m] >= 0:以0为根节点,选择m个候选人的最高性价比
浮点数二分答案mid
DP:
初始化:-inf
定义:dp[u][i]:以u为根选择i个人分数规划求和(01背包选择)
转移:dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k])
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 3010, inf = 1e9;
double q[N], v[N], w[N], dp[N][N];
int n, m, s[N];
vector<vector<int>> g;
//dp[u][i]:战斗力为i时的最大性价比?还要枚举花费
void dfs(int u, int fa){
dp[u][1] = q[u], s[u] = 1;
for(auto v : g[u]){
if(v == fa) continue;
dfs(v,u);
for(int j = min(s[u] + s[v], m); j; j--)
for(int k = max(1, j - s[u]); k <= min(s[v], j-1); k++)
dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k]);
s[u] += s[v];
}
}
bool check(double mid){
for(int i = 1; i <= n; i++) q[i] = v[i] - mid * w[i];
for(int i = 0; i <= n; i++)
for(int j = 0; j <= m; j++)
dp[i][j] = -inf;
dfs(0,0);
return dp[0][m] >= 0;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
cin >> m >> n; m++;
g.resize(n+1);
for(int i = 1; i < n; i++){
cin >> w[i] >> v[i];
int t; cin >> t;
g[i].push_back(t);
g[t].push_back(i);
}
double l = 0, r = 1e4, eps = 0.0003; //浮点数误差处理
while(l + eps < r){
double mid = (r+l) / 2;
if(check(mid)) l = mid + eps;
else r = mid - eps;
}
cout << fixed << setprecision(3) << l << '\n';
return 0;
}

浙公网安备 33010602011771号