0-1分数规划

Posted knife-rose

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了0-1分数规划相关的知识,希望对你有一定的参考价值。

0-1分数规划


 

定义

我们给定两个数组,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)最优比率环

 

1.一般的01分数规划

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;

 

根据上述两个例题可知,二分中的判断函数是根据题目条件自行确定的,主体框架即为 变换不等式+二分答案+某个判断函数是否满足条件

 


 

 


2.最优比率生成树

应用:选择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;


3.最优比率环

应用:在有向图中找到一个环使得环内的点权与边权比值最大(最小)

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分数规划类题目可以多多拓展新的判断条件,不要被已有的题目类型限制,对于不同题目,新的不等式+新的判断函数就是新的分数规划

 
 

 

 

 

以上是关于0-1分数规划的主要内容,如果未能解决你的问题,请参考以下文章

0/1分数规划

0——1分数问题规划

分数规划

POJ - 2976 Dropping tests && 0/1 分数规划

Desert King POJ2728(Prim+迭代+0-1分数规划)

[POJ 2728]Desert King(0-1分数规划/最优比率生成树)