1 条题解
-
0
题解 P4322
前置:分数规划
参考:https://oi-wiki.org/misc/frac-programming/
分数规划用来求一个分式的极值:给出 ,求一组 使 $\displaystyle\frac{\sum_{i=1}^n a_iw_i}{\sum_{i=1}^n b_iw_i}$ 最小化或最大化,再加上一些奇怪的限制。
二分求解
最大值:二分一个答案 ,则:
$$\displaystyle\frac{\sum_{i=1}^n a_iw_i}{\sum_{i=1}^n b_iw_i}>mid \\ \implies\sum_{i=1}^n a_iw_i - mid \times\sum_{i=1}^n b_iw_i>0 \\ \implies\sum_{i=1}^nw_i(a_i-mid \times b_i) > 0$$之后求 的最大值就行了。(比 大则 可行)
最小值同理,求求 的最小值。
例题
给出 ,求一组 使 $\displaystyle\frac{\sum_{i=1}^n a_iw_i}{\sum_{i=1}^n b_iw_i}$ 最大化。
把 作为第 个物品的权值,选择所有 的物品。
const int N = 1e5 + 10; const double eps = 1e-6; int n; double a[N], b[N]; bool check(double mid){ double res = 0; for(int i = 1; i <= n; ++ i) if(a[i] - mid * b[i] > 0) s += a[i] - mid * b[i]; return s > 0; } double solve(){ double l = 0, r = 1e9; while(l + eps < r){ double mid = (l + r) / 2; if(check(mid)) l = mid; else r = mid; } return l; }
前置:树形依赖背包
解
状态 :在以 为根的子树中选 个物品的最大收益。
技巧:如有多个节点为根,新建超级根 。
转移方程:$F[x,t]=\max\limits_{\sum_{i=1}^p~c_i=t-1} \bigg\{\displaystyle\sum\limits_{i=1}^pF[y_i,c_i]\bigg\}+Wei_x$
const int N = 310; int n, m, Wei[N], dp[N][N];//总物品数、选择物品数、物品重量、DP数组 vector<int> Edge[N];//图 void dfs(int x){ dp[x][0] = 0; for(int i = 0; i < Edge[x].size(); ++ i){ int y = Edge[x][i]; dfs(y); for(int t = m; t >= 0; -- t)//当前背包体积 for(int j = 0; j <= t; ++ j)//组内物品 dp[x][t] = max(dp[x][t], dp[x][t-j] + dp[y][j]); } if(x)//超级根 0 不用占物品数 for(int t = m; t > 0; -- t) dp[x][t] = dp[x][t-1] + Wei[x]; return ; }解
把节点重新编号,使得每棵子树的编号最大的节点为它的根。使用后序遍历解决。
void dfs(int x){ siz[x] = 1; for(int i = 0; i < g[x].size(); ++ i){ int y = g[x][i]; dfs(y); siz[x] += siz[y]; } pos[++cnt] = x;//pos[x]为新编号 x 的节点在原来的编号 }然后按着编号顺序 dp 即可。dp 到一个节点,其子节点已经都被 dp 过了。
for(int i = 1; i <= cnt; ++ i) for(int j = 1; j <= k; ++ j) f[i][j] = max(f[i-1][j-1]+val[pos[i]], f[i-siz[pos[i]]][j]);
正文
JSOI2016]最佳团体
给你一棵树,每个节点有 ,选择 个节点使得 $\displaystyle\frac{\sum_{i=1}^k a_i}{\sum_{i=1}^k b_i}$ 最大化。要求此 个节点组成一棵树,且根节点为 ,边方向与原树相同。
转化为 01 分数规划。令 为每个点权值,跑树形背包即可。
这里可以使用预处理时间戳的树形背包,复杂度做到 。
const int N = 2510; int k, n, siz[N], pos[N], cnt; double f[N][N], val[N]; pair<int, int> p[N]; vector<int> g[N]; void dfs(int x){ siz[x] = 1; for(int i = 0; i < g[x].size(); ++ i){ int y = g[x][i]; dfs(y); siz[x] += siz[y]; } pos[cnt] = x; } bool check(double mid){ for(int i = 0; i <= cnt; ++ i) for(int j = 1; j <= k; ++ j) f[i][j] = -1e9; for(int i = 1; i <= n; ++ i) val[i] = (double) p[i].first - mid * p[i].second; for(int i = 1; i <= cnt; ++ i) for(int j = 1; j <= k; ++ j) f[i][j] = max(f[i-1][j-1]+val[pos[i]], f[i-siz[pos[i]]][j]); return f[cnt][k] > 0;//用cnt,不能用n(因为n不包括节点0) } double solve(){ dfs(0); ++ k; double l = 0, r = 1e5; while(l + 1e-5 <= r){ double mid = (l + r) / 2; if(check(mid)) l = mid; else r = mid; } return l; }
- 1
信息
- ID
- 6418
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 9
- 已通过
- 3
- 上传者