考场上能想出来是二分,公式也推出来了,就是不会验证了….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<cstdio>
#include<algorithm>
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;
}
本文介绍了一种利用零一分数规划解决特定问题的方法,并通过一个具体的编程实例详细展示了如何运用二分查找法来找到最优解。文章解释了如何构建数学模型、如何验证解的有效性以及如何实现代码。
828

被折叠的 条评论
为什么被折叠?



