洛谷P4322.最佳團體
阿新 • • 發佈:2022-03-04
題目大意
一個 \(n(1\leq n\leq 2500)\) 個節點的森林,每個點 \(i\) 有權值 \(s_{i},p_{i}(0<s_{i},p_{i}\leq 10^4)\) 以及父親 \(r_{i}\) 。每個節點可以被選擇的前提是其父親已經被選擇,從中選出 \(k(1\leq k\leq n)\) 個節點,使得 \(\sum_{j=1}^{k}\frac{p_{j}}{s_{j}}\) 的值最大,求出這個值。
思路
這很顯然是一個分數規劃問題,我們可以讓每個節點 \(i\) 的權值為 \(p_{i}-s_{i}\times mid\) ,然後二分來判斷。我們用節點 \(0\) 作為根把所有森林連起來變成一棵樹,每次判斷時作樹形 \(dp\)
每次 \(dp\) 時將所有 \(dp[v,0]\) 初始為 \(0\) ,其餘為 \(-inf\) 。最後根據 \(dp[0,k]\) 是否 \(\geq0\) 來判斷即可,每次 \(check\) 可以 \(O(n^2)\)
程式碼
#include<bits/stdc++.h> #include<unordered_map> #include<unordered_set> using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PII; #define all(x) x.begin(),x.end() //#define int LL //#define lc p*2+1 //#define rc p*2+2 #define endl '\n' #define inf 0x3f3f3f3f #define INF 0x3f3f3f3f3f3f3f3f #pragma warning(disable : 4996) #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0) const double eps = 1e-8; const LL MOD = 1000000007; const LL mod = 998244353; const int maxn = 2510; vector<int>G[maxn]; LL K, N, S[maxn], P[maxn], R[maxn]; double dp[maxn][maxn]; double val[maxn]; LL vsize[maxn]; void add_edge(int from, int to) { G[from].push_back(to); } void dfs(int v) { vsize[v] = 1; dp[v][0] = 0; if (!G[v].size()) { dp[v][1] = val[v]; return; } for (int i = 0; i < G[v].size(); i++) { int to = G[v][i]; dfs(to); for (int j = vsize[v] - 1; j >= 0; j--) { for (int k = vsize[to]; k >= 0; k--) dp[v][j + k] = max(dp[v][j + k], dp[v][j] + dp[to][k]); } vsize[v] += vsize[to]; } if (v) { for (int i = vsize[v]; i > 0; i--) dp[v][i] = dp[v][i - 1] + val[v]; } } bool check(double x) { for (int i = 0; i <= N; i++) { for (int j = 1; j <= K; j++) dp[i][j] = -inf; } for (int i = 1; i <= N; i++) val[i] = P[i] - S[i] * x; dfs(0); if (dp[0][K] - 0 > -eps) return true; return false; } void solve() { double lo = 0, hi = inf; for (int i = 1; i <= 100; i++) { double mid = (lo + hi) / 2; if (check(mid)) lo = mid; else hi = mid; } cout << setiosflags(ios::fixed) << setprecision(3) << lo << endl; } int main() { IOS; cin >> K >> N; for (int i = 1; i <= N; i++) { cin >> S[i] >> P[i] >> R[i]; add_edge(R[i], i); } solve(); return 0; }