标签:col 选择 size 表示 lse 形式 负环 分数规划 find
我们给定两个数组,a[i]表示选取i的收益,b[i]表示选取i的代价。
如果选取i,定义x[i]=1否则x[i]=0。每个物品只有选和不选的两种方案,求一个选择的方案使得R=sigma(a[i]x[i])/sigma(b[i]x[i]),也就是选择物品的总收益/总代价最大或者最小。
(1)一般01分数规划
(2)最优比率生成树
(3)最优比率环
https://www.luogu.org/problemnew/show/P1570
题目概述:有n杯种调料,每种调料有美味值a[i]和时间b[i],从中选择m种使得sigma(a[i])/sigma(b[i])最大;
初步分析:
贪心?很容易构造反例
DP?小数又很难实现
我们不妨分析一下目标函数
我们设sigma(a[i]x[i])/sigma(b[i]x[i])=t,问题即为求t的最大值(x[i]为0或1,表示第i种调料选或不选)
将分母移向变成sigma(a[i]x[i])-t*sigma(b[i]x[i])>=0时t的最大值
设上面函数为f(t),继续变形得f(t)=sigma(a[i]x[i]-t*b[i]x[i])
提取x[i]得f(t)=sigma(x[i](a[i]-t*b[i]))
此时观察发现对于特定的一组选择(即一组x[i]),函数必定是单调的,我们可以通过二分答案求得t的数值
对面的f(t)的判断,设c[i]为a[i]-t*b[i],此时必定取前m大的c[i]之和是最优的,只需判断前m大c[i]之和是否大于0即可得知t是否可取
while(r-l>1e-6) { double mid=(l+r)/2; for(int i=1;i<=n;++i) { c[i]=a[i]-mid*b[i]; } sort(c+1,c+n+1); double miao=0; for(int i=n;i>n-m;--i) { miao+=c[i]; } if(miao>=0) l=mid; else r=mid; }
https://www.luogu.org/problemnew/show/P4377
题目概述:不要求选m种,但是每个物品有一个w[i],要求选择的物品w[i]之和大于m,求sigma(a[i])/sigma(b[i])最大。
对于式子的变换此题与上题相同,此时贪心的判断f(t)是否可取不可行了,因为有可能你要先选一个价值较小但重量很大的牛撑场面,再选价值大的牛
我们可以考虑背包!只要f[m]>0即可取
inline bool check() { memset(f,-0x3f,sizeof(f)); f[0]=0; for(int i=1;i<=n;++i) { for(int j=m;j>=0;--j) { int q=j+w[i]; if(q>m) q=m; f[q]=max(f[q],f[j]+c[i]); } } return f[m]>=0; }
https://www.luogu.org/problemnew/show/P2115
题目概述:一个数列,选择其中一段连续期间去掉(规定头和尾不能去掉),求剩下数字平均值的最小值;
首先区间类问题可以联想到前缀和,设sum[i]为前缀和数组
方程可以列为sum[n]-(sum[j]-sum[i-1])/(n-(j-i+1))=t 求t最小值
变形得到sum[n]-t*n-(sum[j]-t*j)+sum[i-1]-t*(i-1)>=0
我们的分数规划有这么多冗余变量是很难计算的!考虑合并 观察发现我们可以令c[i]=sum[i]-t*i;
原式子变为c[n]-c[j]+c[i-1]>=0
这时考虑问题:如何求最小值?由于本题目的实际意义,在某个较小答案区间内是不存在任何解的,所以按照原来的如果无解就r=mid会令答案始终为0
所以我们要换一种考虑方式:由于是最小值,所以无论破坏哪一段区间对于t都应该满足判断条件,所以我们只需要求出满足所有判断条件的t的最大值即可!!!
也就是说,如果找到一组c[j]-c[i-1]>c[n],那么这个t值就凉了QwQ
我们可以求一个前缀最大值,一个后缀最小值,枚举每一位i,判断maxn[i]-minn[i]是否大于c[n]即可(枚举时去掉首和尾)
inline bool check(double t) { for(int i=1;i<=n;++i) c[i]=sum[i]-t*i; for(int i=1;i<=n-2;++i) minn[i]=min(minn[i-1],c[i]);//前缀最小值 for(int i=n-1;i>=2;--i) maxn[i]=max(maxn[i+1],c[i]);//后缀最大值 for(int i=2;i<n;++i) if(maxn[i]-minn[i-1]>c[n]) return 0; return 1; } r=(sum[1]+sum[n]-sum[n-1])/2.0; maxn[n]=-0x7fffffff; while(r-l>1e-6) { mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; }
根据上述两个例题可知,二分中的判断函数是根据题目条件自行确定的,主体框架即为 变换不等式+二分答案+某个判断函数是否满足条件
应用:选择n-1条边使图变成完全图,求点权与边权比值的最大(小)值
https://www.luogu.org/problemnew/show/P4951
题目概述:有一只施工队要修路,每条路有成本c和需要的时间t,总收入为k,要求选择数条边使图联通,求k-sigma(c[i]x[i])/sigma(t[i]x[i])=ans的最大值 (x[i]为0或1表示选或不选)
分析:
将分母移项得f(ans)=ans*sigma(t[i]x[i])+sigma(c[i]x[i])-k;
提取x[i]: f(ans)=sigma(x[i](ans*t[i]-c[i]))-k;
当ans能取到时需满足f(ans)>=0
将ans二分答案,ans*t[i]-c[i]作为边权,每次求最小生成树即可。
while(r-l>1e-6) { clear(); mid=(l+r)/2; for(int i=1;i<=m;++i) { a[i].val=a[i].t*mid+a[i].c; } sort(a+1,a+m+1,cmp); for(int i=1;i<=m;++i) { int tx=find(a[i].x),ty=find(a[i].y); if(tx!=ty) { f[tx]=ty; res+=a[i].val; if(++tot==n-1) break; } } if(res<=k) l=mid; else r=mid; }
应用:在有向图中找到一个环使得环内的点权与边权比值最大(最小)
https://www.luogu.org/problemnew/show/P2868
题目概述:每个点有点权,每条边有边权,求一个环,使得环内sigma(点权)/sigma(边权)最大,求这个最大值
设每个点点权为a[i],每条边边权为b[i]; 假设一个环内的sigma(a[i])/sigma(b[i])=t,问题即为求t的最大值,可以拓展为01分数规划问题
那么怎么怎么规划呢qwq
还是首先变形式子:
原问题等价于sigma(a[i])-t*sigma(b[i])>=0时t的最大值
设f(t)=sigma(a[i])-t*sigma(b[i]);
f(t)=sigma(a[i]-t*b[i])
要求f(t)>=0时t的最大值
只要求-f(t)<=0时t的最大值
即sigma(t*b[i]-a[i])<=0时t的最大值
发现了吗!只要以c[i]=t*c[i]-a[i]为边权,判断图中是否存在负环即可!!
判负环方法很多这里不详细讲了,但是本蒟蒻做题发现这类问题dfs判负环通常快的一
inline void spfa(int now) { vis[now]=1; for(int i=head[now];i;i=a[i].nxt) { int t=a[i].to; if(dis[t]>dis[now]-v[t]+mid*a[i].val) { if(vis[t]||flag) { flag=1; break; } dis[t]=dis[now]-v[t]+mid*a[i].val; spfa(t); } } vis[now]=0; } while(r-l>1e-5) { memset(dis,0,sizeof(dis)); mid=(l+r)/2; flag=0; for(int i=1;i<=n;++i) { spfa(i); if(flag) break; } if(flag) l=mid; else r=mid; }
其实我们可以发现,最优比率生成树和最优比率环只是一种比较特殊的判断函数,其本质还是 变换不等式+二分答案+判断函数
希望各位做题的时候遇到01分数规划类题目可以多多拓展新的判断条件,不要被已有的题目类型限制,对于不同题目,新的不等式+新的判断函数就是新的分数规划
标签:col 选择 size 表示 lse 形式 负环 分数规划 find
原文地址:https://www.cnblogs.com/knife-rose/p/11172120.html