【洛谷】P1642 规划 (01分数规划)
\(01\) 分数规划好题。
前置芝士:\(01\) 分数规划模型
给定整数 \(a_1\),\(a_2\),···,\(a_n\) 以及 \(b_1\),\(b_2\),···,\(b_n\),求一组解 \(x_i\) \((\) \(1 \leq i \leq n\),\(x_i=0\) 或 \(x_i=1\) \()\) ,使得下式最大化:
\(\dfrac{ {\textstyle \sum_{i=1}^{a}} a_i*x_i}{ {\textstyle \sum_{i=1}^{b}} b_i*x_i}\) 。
用自己的话(lyd大佬)的话来说,就是给定 \(n\) 对整数 \(a_i,b_i\) ,从中选出若干对,使选出的数对的 \(a\) 之和与 \(b\) 之和的商最大。
\(01\) 分数规划的基本做法:
取一个数值 \(mid\),然后判断是否存在一组可行解满足:
\(\dfrac{ {\textstyle \sum_{i=1}^{n}}a_i*x_i }{ {\textstyle \sum_{i=1}^{n}b_i*x_i} } \geq mid\)。
直接求解比较困难,于是可以移项,得:
\({\textstyle \sum_{i=1}^{n} a_i*x_i} \geq {\textstyle \sum_{i=1}^{n}} b_i*x_i*mid\)。
继续移项,得:
\({\textstyle \sum_{i=1}^{n}(a_i-b_i*mid)*x_i} \geq 0\) 。
而最终的答案就是 \(mid\) 的最大值。可以发现,这与二分答案非常相似,事实上,\(01\) 分数规划问题就是需要借助二分答案的方法进行求解。但是别忘了,可以使用二分答案的前提条件是满足单调性,而对于 \(01\) 分数规划来说,要求的答案 \(mid\) 显然存在单调性。 因为如果发现了一个可行的 \(mid\) ,那么肯定要往更大的方向去寻找是否存在更优的 \(mid\) 。
题意
给定一棵 \(n\) 个节点树,每个节点都有一个 \(a_i\) 和 \(b_i\) 值,现在要删去 \(m\) 个点,在满足剩下的节点仍然能形成一颗树的前提下,求:
\(\dfrac{\sum a_i}{\sum b_i}\) 的最大值。
思路
初看本题,会发现题意和 \(01\) 分数规划模型非常像,但是题目还有一个额外要求,剩下的子节点仍然能形成一棵树。于是本题需要和树形 \(dp\) 结合起来做。
首先根据 \(01\) 分数规划的基本做法,先枚举可能的答案。根据题意,当所有的 \(a_i=10000\) ,\(b_I=1\) ,答案会取到上界 \(10000\) ,故最初的右端点 \(r=10000\) 。同时为了防止出错也可以将 \(r\) 值适当上调。
判断一个 \(mid\) 是否存在可行解时,可以将树上每个点的值都变成 \(a_i-mid*b_i\) 。这样就可以将判断转化为一个十分经典的树上背包问题:选择连续的 \(n-m\)个节点,判断其中的最大值是否 \(\geq 0\) 。如果存在,就令 \(l=mid\) ,反之,则令 \(r=mid\) 。最终得到的就是 \(mid\) 的最大值,也就是本题的答案。(二分答案的基本做法)
code:
#include<iostream>
using namespace std;
const int N=110;
const int INF=0x3f3f3f3f;
const double eps=1e-4;
struct edge{
int v,w,nex;
}e[N<<1];
int size[N],h[N],idx,a[N],b[N],n,m;
double d[N],f[N][N];
void add(int u,int v)
{
e[++idx].v=v;
e[idx].nex=h[u];
h[u]=idx;
}
void dfs(int u,int fa)
{
size[u]=1;
f[u][0]=0;
for(int i=h[u];i;i=e[i].nex)
{
int v=e[i].v;
if(v==fa) continue;
dfs(v,u);
size[u]+=size[v];
for(int j=min(size[u],m);j>=0;j--)
for(int k=0;k<=min(j,size[v]);k++)
f[u][j]=max(f[u][j],f[v][k]+f[u][j-k]);
}
for(int i=min(size[u],m);i>=1;i--) f[u][i]=f[u][i-1]+d[u];
}
bool check(double mid)
{
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
f[i][j]=-INF;
for(int i=1;i<=n;i++) d[i]=a[i]*1.0-mid*(b[i]*1.0);
dfs(1,0);
for(int i=1;i<=n;i++)
if(f[i][m]>-eps) return true;
return false;
}
int main()
{
scanf("%d%d",&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 u,v,i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
m=n-m;
double l=0,r=10010;
while(r-l>eps)
{
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.1lf\n",r);
return 0;
}

浙公网安备 33010602011771号