yuwj  

前置知识: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;
}
总之,很板的一题
posted on 2025-04-13 10:33  xiaowang524  阅读(15)  评论(0)    收藏  举报