XJOI 挖金矿(01分数规划)

XJOI 挖金矿(01分数规划)_第1张图片
XJOI 挖金矿(01分数规划)_第2张图片
考场上能想出来是二分,公式也推出来了,就是不会验证了….MDZZ。
这个题有个奇葩的地方:“h*n<=十万”
所以,这个坑爹题可以用一维数组存。
让num[(i-1)*n+j]来作为第i行第j个的数值。
然后,我们是需要求前缀和的,当j==1的时候,直接让当前下标的值等于输入的值,其余情况还是向正常求前缀和那么求。

所以,零一分数规划呢?
开始!

i=1nv[i]d[i] )。)/( i=1nd[i] )==R
这里的v数组是指每个格子的值,d数组只有0和1,代表这个格子选不选。
这里的R是我们的最终解!
式子移一下,就成了:
i=1nv[i]d[i] )。)-R*( i=1nd[i] )==0

设f(L)=( i=1nv[i]d[i] )。)-L*( i=1nd[i] )

当f(L)>0时,( i=1nv[i]d[i] )。)-L*( i=1nd[i] )>0
i=1nv[i]d[i] )。)>L*( i=1nd[i] )
i=1nv[i]d[i] )。)/( i=1nd[i] )

L

所以,当f(L)>0时,我们会得到一个比L更大的解,所以L需要增大。
那么二分的时候,将一个L带进去验证,如果,f(L)>0,则l=mid,else r=mid。

这要怎么验证呢?
对于每一列,我们可以枚举往下挖多少深度,反正h*n最大是十万,时间复杂度是O(n *k *log INF(是一个超不过1000的常数)),然后求出这一列平均值的最大值。将每一列平均值的最大值加起来就是我们的f(L)

这就完了~关于01分数规划,可以戳泥泞的道路

#include
#include
using namespace std;
typedef double db;
const db inf=0x7fffffffff;
db qzh[1000000];
int n,h;
bool can(db mid)
{
    db ans=0;
    for(int i=1;i<=n;i++)
    {
        db s=inf*(-1);
        for(int j=1;j<=h;j++)
        {
            db hah=j;
            s=max(qzh[(i-1)*h+j]-hah*mid,s);
        }
        ans+=s;
    }
    if(ans>=0)
        return true;
    return false;
}
db div()
{
    db l=0;
    db r=100000000000.0;
    int k=500;
    while(k--)
    {
        db mid=(l+r)/2;
        if(can(mid))
        {
            l=mid;
        }
        else
        {
            r=mid;
        }
    }
    return l;
}
int main()
{
    scanf("%d%d",&n,&h);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=h;j++)
        {
            db x;
            scanf("%lf",&x);
            if(j!=1)
            qzh[(i-1)*h+j]=qzh[(i-1)*h+j-1]+x;
            else 
            qzh[(i-1)*h+j]=x;
        }
    }
    printf("%.4lf",div());
    return 0;
}

你可能感兴趣的:(===二分===)